diff options
41 files changed, 1518 insertions, 356 deletions
@@ -1,5 +1,64 @@ 2002-06-14 Baptiste Lepilleur <gaiacrtn@free.fr> + * include/cppunit/plugin/PlugInManager.h: + * src/cppunit/PlugInManager.cpp: added two methods to use the plug-in + interface for Xml Outputter hooks. + + * include/cppunit/plugin/TestPlugIn.h: added two methods to the plug-in + interface for Xml Outputter hooks. + + * include/cppunit/plugin/TestPlugInAdapter.h: + * src/cppunit/plugin/TestPlugInAdapter.cpp: renamed TestPlugInDefaultImpl. + Added empty implementation for Xml outputter hook methods. + + * include/cppunit/tools/StringTools.h: + * src/cppunit/tools/StringTools.cpp: added. Functions to manipulate string + (conversion, wrapping...) + + * include/cppunit/tools/XmlElement.h: + * src/cppunit/XmlElement.cpp: renamed addNode() to addElement(). Added + methods to walk and modify XmlElement (for hook). Added documentation. + Use StringTools. + + * include/cppunit/XmlOutputter.h: + * src/cppunit/XmlOutputter.cpp: added hook calls & management. + + * include/cppunit/XmlOutputterHook.h: + * src/cppunit/XmlOutputterHook.cpp: added. Hook to customize XML output. + + * src/DllPlugInTester/DllPlugInTester.cpp: call plug-in XmlOutputterHook + when writing XML output. Modified so that memory is freed before + unloading the test plug-in (caused crash when freeing the XmlDocument). + + * examples/ReadMe.txt: + * examples/ClockerPlugIn/ReadMe.txt: added details about the plug-in + (usage, xml content...) + + * examples/ClockerPlugIn/ClockerModel.h: + * examples/ClockerPlugIn/ClockerModel.cpp: extracted from ClockerListener. + Represents the test hierarchy and tracked time for each test. + + * examples/ClockerPlugIn/ClockerListener.h: + * examples/ClockerPlugIn/ClockerListener.cpp: extracted test hierarchy + tracking to ClockerModel. Replaced the 'flat' view option with a 'text' + option to print the timed test tree to stdout. + + * examples/ClockerPlugIn/ClockerPlugIn.cpp: updated to hook the XML + output and use the new classes. + + * examples/ClockerPlugIn/ClockerXmlHook.h: + * examples/ClockerPlugIn/ClockerXmlHook.cpp: added. XmlOutputterHook to + includes the timed test hierarchy and test timing in the XML output. + + * examples/cppunittest/XmlElementTest.h: + * examples/cppunittest/XmlElementTest.cpp: added new test cases. + + * examples/cppunittest/XmlOutputterTest.h: + * examples/cppunittest/XmlOutputterTest.cpp: added tests for + XmlOutputterHook. + +2002-06-14 Baptiste Lepilleur <gaiacrtn@free.fr> + * src/cppunit/TypeInfoHelper.cpp: added work around for bug #565481. gcc 3.0 RTTI name() returns the type prefixed with a number (the length of the type). The work around strip the number. @@ -1,10 +1,14 @@ New in CppUnit 1.9.8: --------------------- - - XmlDocument - Custom test macros. - - Exception message - - MFC TestRunner + - Exception message are now structured + - MFC TestRunner, detail field added. + - XmlDocument extracted from XmlOutputter to create different XML output + - Xml output customization. + - Test plug-in XML output Hook + - ClockerPlugIn example includes test time in XML output. + - DllPlugInTester allows test plug-in to hook the XML output. * Custom test macros @@ -37,25 +41,55 @@ This change allow ouputters to deal with all failure the same way (there is no special case for the equality assertion any more). +* XmlDocument extracted from XmlOutputter to create different XML output + + - Classes XmlDocument and XmlElement where extracted from XmlOutputter. This + help writing outputters that use a completly different XML format. + +* Xml output customization. + + - Xml output can be customized using XmlOutputterHook. To do so, subclass + XmlOutputterHook and register it to the XmlOutputter with addHook() before + call XmlOutputter::write(). + + Hook can be used to add some datas to the XmlDocument or the XmlElement of + a specific hook. Methods have been added to XmlElement to help walking and + modifying the XmlDocument. + + See ClockerPlugIn example. + * MFC TestRunner + - The name of the test is displayed just before being run. - Browse dialog is resizable - Better (and cleaner) handling of windows resizing - Failure list on show the short description of the failure. - Edit field added to display the details of the failure. +* MFC test plug-in runner (TestPlugInRunner): + + - command line: a dll name can be specified on the command after -testsuite: + example: TestPlugInRunnerd.exe -testsuite Simpled.dll + +* Test plug-in XML output Hook + + - TestPlugIn interface provides a mean for plug-in to register hook for + XML output. Practically, this allow plug-in to add specific data to the + output. See ClockerPlugIn example, which add timing datas to the xml + output. + * DllPlugInTester: - added option -w / --wait to wait for the user to press a key before exiting. -* MfcUi: + - plug-in can now provides XmlOutputterHook to add specific datas to the + XML ouput. See ClockerPlugIn example. - - the name of the test is displayed just before being run. +* Examples: -* MfcUi plug-in runner (TestPlugInRunner): - - - command line: a dll name can be specified on the command after -testsuite: - example: TestPlugInRunnerd.exe -testsuite Simpled.dll + - ClockerPlugIn: the example now use the new XmlOutputterHook. Test time are + now included in the XML output. See examples/ClockerPlugIn/ReadMe.txt for + details. * Bug Fix: @@ -72,6 +106,9 @@ * Compatibility Break: + - CompilerOutputter: removed printNotEqualMessage() and printDefaultMessage(). + No longer needed since Exception message are processed in a generic way. + - Exception constructor takes a Message instead of a string. Notes that the first argument in Message constructor is a short description, not the message. Therefore, the change will usualy have the following form: @@ -79,13 +116,7 @@ You may want to use Asserter functions instead of constructing and throwing the exception manually. - - CompilerOutputter: removed printNotEqualMessage() and printDefaultMessage(). - No longer needed since Exception message are processed in a generic way. - - - XmlOutputter: removed methods writeProlog() and writeTestResult() which - are replaced by XmlDocument. - - - XmlOuputter::Node: class has been extracted and renamed XmlElement. + - TestPlugInAdapter: renamed TestPlugInDefaultImpl. - TestSuiteBuilder: removed default constructor. All remaining constructors take an additional argument of type TestNamer used to specify the fixture @@ -96,6 +127,13 @@ - TextTestResult: most printing method were removed. This task is now delegated to TextOuputter. + - XmlElement: renamed addNode() to addElement(). + + - XmlOutputter: removed methods writeProlog() and writeTestResult() which + are replaced by XmlDocument. + + - XmlOuputter::Node: class has been extracted and renamed XmlElement. + * Deprecated: - Asserter: all functions that use a string for the failure message. Construct @@ -1,5 +1,4 @@ * CppUnit: - - Update MfcTestRunner to use TestPath to store test in the registry - Provide a mean for the user to catch 'custom' exception in TestCase::run (exception that do not subclass std::exception, such as MFC CException, or RogueWave RWXMsg). @@ -8,8 +7,6 @@ (test & doc level). - Allow test plug-in to 'hook' the XmlOutputter when used. - TextUi::TestRunner should use CppUnit::TestRunner as a base class - - Modify MfcUi::TestRunner to expose TestResult (which allow specific TestListener - for global initialization). - [DONE] Make Exception message more flexible: introduce a Message object which contains: - a short description ('assertion failed', 'equality assertion failed'...) - detail strings: "Expected : 3", "Actual : 5"... @@ -27,6 +24,9 @@ - add tests for test plug-in * VC++ TestRunner: + - Modify MfcUi::TestRunner to expose TestResult (which allow specific TestListener + for global initialization). + - Update MfcTestRunner to use TestPath to store test in the registry - [DONE] Add "details" field to show detail of the selected failed test: - suite and test name, - failure message. If possible separate "was" and "expected" in the diff --git a/examples/ClockerPlugIn/ClockerListener.cpp b/examples/ClockerPlugIn/ClockerListener.cpp index 9ac9c52..a93cd38 100644 --- a/examples/ClockerPlugIn/ClockerListener.cpp +++ b/examples/ClockerPlugIn/ClockerListener.cpp @@ -6,13 +6,14 @@ #include <cppunit/Test.h> #include <iostream> #include "ClockerListener.h" +#include "ClockerModel.h" #include <stdio.h> -ClockerListener::ClockerListener( bool flatten ) - : m_flatten( flatten ) - , m_testCount( 0 ) - , m_totalTestCaseTime( 0 ) +ClockerListener::ClockerListener( ClockerModel *model, + bool text ) + : m_model( model ) + , m_text( text ) { } @@ -26,121 +27,79 @@ void ClockerListener::startTestRun( CppUnit::Test *test, CppUnit::TestResult *eventManager ) { - m_tests.reserve( test->countTestCases() *2 ); + m_model->setExpectedTestCount( test->countTestCases() *2 ); } void -ClockerListener::startTest( CppUnit::Test *test ) -{ - enterTest( test, false ); - ++m_testCount; -} - - -void -ClockerListener::endTest( CppUnit::Test *test ) -{ - exitTest( test, false ); -} - - -void -ClockerListener::startSuite( CppUnit::Test *suite ) +ClockerListener::endTestRun( CppUnit::Test *test, + CppUnit::TestResult *eventManager ) { - enterTest( suite, true ); + if ( m_text ) + printStatistics(); } void -ClockerListener::endSuite( CppUnit::Test *suite ) +ClockerListener::startTest( CppUnit::Test *test ) { - exitTest( suite, true ); + m_model->enterTest( test, false ); } void -ClockerListener::endTestRun( CppUnit::Test *test, - CppUnit::TestResult *eventManager ) +ClockerListener::endTest( CppUnit::Test *test ) { - printStatistics(); + m_model->exitTest( test, false ); } void -ClockerListener::enterTest( CppUnit::Test *test, - bool isSuite ) +ClockerListener::startSuite( CppUnit::Test *suite ) { - m_currentPath.add( test ); - - int testIndex = m_tests.size(); - if ( !m_testIndexes.empty() ) - m_tests[ m_testIndexes.top() ].m_childIndexes.push_back( testIndex ); - m_testIndexes.push( testIndex ); - - TestInfo info; - info.m_timer.start(); - info.m_path = m_currentPath; - info.m_isSuite = isSuite; - - m_tests.push_back( info ); + m_model->enterTest( suite, true ); } void -ClockerListener::exitTest( CppUnit::Test *test, - bool isSuite ) +ClockerListener::endSuite( CppUnit::Test *suite ) { - m_tests[ m_testIndexes.top() ].m_timer.finish(); - if ( !isSuite ) - m_totalTestCaseTime += m_tests.back().m_timer.elapsedTime(); - - m_currentPath.up(); - m_testIndexes.pop(); + m_model->exitTest( suite, true ); } void ClockerListener::printStatistics() const { - printTest( m_tests[0], "" ); + printTest( 0, "" ); std::cout << std::endl; std::cout << "Total elapsed time: "; - printTestTime( m_tests[0].m_timer.elapsedTime() ); + printTime( m_model->totalElapsedTime() ); std::cout << ", average test case time: "; - double average = 0; - if ( m_testCount > 0 ) - average = m_totalTestCaseTime / m_testCount; - printTestTime( average ); + printTime( m_model->averageTestCaseTime() ); } void -ClockerListener::printTest( const TestInfo &info, +ClockerListener::printTest( int testIndex, const std::string &indentString ) const { std::string indent = indentString; const int indentLength = 3; - if ( !m_flatten ) - printTestIndent( indentString, indentLength ); - - printTestTime( info.m_timer.elapsedTime() ); - - if ( m_flatten ) - printFlattenedTestName( info ); - else - printTestName( info ); + printTestIndent( indentString, indentLength ); + printTime( m_model->testTimeFor( testIndex ) ); + std::cout << m_model->testPathFor( testIndex ).getChildTest()->getName(); std::cout << std::endl; - if ( info.m_childIndexes.empty() ) + if ( m_model->childCountFor( testIndex ) == 0 ) indent+= std::string( indentLength, ' ' ); else indent+= "|" + std::string( indentLength -1, ' ' ); - for ( int index =0; index < info.m_childIndexes.size(); ++index ) - printTest( m_tests[ info.m_childIndexes[ index ] ], indent ); + for ( int index =0; index < m_model->childCountFor( testIndex ); ++index ) + printTest( m_model->childAtFor( testIndex, index ), indent ); } @@ -158,36 +117,7 @@ ClockerListener::printTestIndent( const std::string &indent, void -ClockerListener::printTestTime( double elapsedSeconds ) const -{ - char buffer[320]; - const char *format; - if ( elapsedSeconds < 1 ) - format = "(%2.3fs) "; - else if ( elapsedSeconds < 10 ) - format = "(%3.2fs) "; - else if (elapsedSeconds < 100 ) - format = "(%4.1fs) "; - else - format = "(%5fs) "; - - ::sprintf( buffer, format, elapsedSeconds ); - - std::cout << buffer; -} - - -void -ClockerListener::printFlattenedTestName( const TestInfo &info ) const -{ - std::cout << info.m_path.toString(); - if ( info.m_isSuite ) - std::cout << "/"; -} - - -void -ClockerListener::printTestName( const TestInfo &info ) const +ClockerListener::printTime( double time ) const { - std::cout << info.m_path.getChildTest()->getName(); + std::cout << '(' << ClockerModel::timeStringFor( time ) << "s) "; } diff --git a/examples/ClockerPlugIn/ClockerListener.h b/examples/ClockerPlugIn/ClockerListener.h index b256380..e3c84e4 100644 --- a/examples/ClockerPlugIn/ClockerListener.h +++ b/examples/ClockerPlugIn/ClockerListener.h @@ -7,28 +7,25 @@ #define CLOCKERLISTENER_H #include <cppunit/TestListener.h> -#include <cppunit/TestPath.h> -#include <stack> -#include <vector> -#ifdef CLOCKER_USE_WINNTTIMER -#include "WinNtTimer.h" -typedef WinNtTimer Timer; -#else -#include "Timer.h" -#endif +class ClockerModel; + /// TestListener that prints a flatten or hierarchical view of the test tree. class ClockerListener : public CppUnit::TestListener { public: - ClockerListener( bool flatten ); + ClockerListener( ClockerModel *model, + bool text ); virtual ~ClockerListener(); void startTestRun( CppUnit::Test *test, CppUnit::TestResult *eventManager ); + void endTestRun( CppUnit::Test *test, + CppUnit::TestResult *eventManager ); + void startTest( CppUnit::Test *test ); void endTest( CppUnit::Test *test ); @@ -37,37 +34,16 @@ public: void endSuite( CppUnit::Test *suite ); - void endTestRun( CppUnit::Test *test, - CppUnit::TestResult *eventManager ); - private: - struct TestInfo - { - CppUnit::TestPath m_path; - Timer m_timer; - bool m_isSuite; - std::vector<int> m_childIndexes; - }; - - void enterTest( CppUnit::Test *test, - bool isSuite ); - - void exitTest( CppUnit::Test *test, - bool isSuite ); - void printStatistics() const; - void printTest( const TestInfo &info, + void printTest( int testIndex, const std::string &indentString ) const; void printTestIndent( const std::string &indent, const int indentLength ) const; - void printTestTime( double elapsedSeconds ) const; - - void printFlattenedTestName( const TestInfo &info ) const; - - void printTestName( const TestInfo &info ) const; + void printTime( double time ) const; /// Prevents the use of the copy constructor. ClockerListener( const ClockerListener &other ); @@ -76,14 +52,8 @@ private: void operator =( const ClockerListener &other ); private: - bool m_flatten; - CppUnit::TestPath m_currentPath; - - int m_testCount; - double m_totalTestCaseTime; - - std::stack<int> m_testIndexes; - std::vector<TestInfo> m_tests; + ClockerModel *m_model; + bool m_text; }; diff --git a/examples/ClockerPlugIn/ClockerModel.cpp b/examples/ClockerPlugIn/ClockerModel.cpp new file mode 100644 index 0000000..60e87ad --- /dev/null +++ b/examples/ClockerPlugIn/ClockerModel.cpp @@ -0,0 +1,145 @@ +// ////////////////////////////////////////////////////////////////////////// +// Implementation file ClockerModel.cpp for class ClockerModel +// (c)Copyright 2000, Baptiste Lepilleur. +// Created: 2002/06/14 +// ////////////////////////////////////////////////////////////////////////// +#include "ClockerModel.h" + + +ClockerModel::ClockerModel() + : m_testCaseCount( 0 ) + , m_totalTestCaseTime( 0 ) +{ +} + + +ClockerModel::~ClockerModel() +{ +} + + +void +ClockerModel::setExpectedTestCount( int count ) +{ + m_tests.reserve( count ); +} + + +void +ClockerModel::enterTest( CppUnit::Test *test, + bool isSuite ) +{ + m_currentPath.add( test ); + + int testIndex = m_tests.size(); + if ( !m_testIndexes.empty() ) + m_tests[ m_testIndexes.top() ].m_childIndexes.push_back( testIndex ); + m_testIndexes.push( testIndex ); + m_testToIndexes.insert( TestToIndexes::value_type( test, testIndex ) ); + + TestInfo info; + info.m_timer.start(); + info.m_path = m_currentPath; + info.m_isSuite = isSuite; + + m_tests.push_back( info ); + + if ( !isSuite ) + ++m_testCaseCount; +} + + +void +ClockerModel::exitTest( CppUnit::Test *test, + bool isSuite ) +{ + m_tests[ m_testIndexes.top() ].m_timer.finish(); + if ( !isSuite ) + m_totalTestCaseTime += m_tests.back().m_timer.elapsedTime(); + + m_currentPath.up(); + m_testIndexes.pop(); +} + + +double +ClockerModel::totalElapsedTime() const +{ + return m_tests[0].m_timer.elapsedTime(); +} + + +double +ClockerModel::averageTestCaseTime() const +{ + double average = 0; + if ( m_testCaseCount > 0 ) + average = m_totalTestCaseTime / m_testCaseCount; + return average; +} + + +double +ClockerModel::testTimeFor( int testIndex ) const +{ + return m_tests[ testIndex ].m_timer.elapsedTime(); +} + + +std::string +ClockerModel::timeStringFor( double time ) +{ + char buffer[320]; + const char *format; + if ( time < 1 ) + format = "%2.3f"; + else if ( time < 10 ) + format = "%3.2f"; + else if (time < 100 ) + format = "%4.1f"; + else + format = "%6f"; + + ::sprintf( buffer, format, time ); + + return buffer; +} + + +bool +ClockerModel::isSuite( int testIndex ) const +{ + return m_tests[ testIndex ].m_isSuite; +} + + +const CppUnit::TestPath & +ClockerModel::testPathFor( int testIndex ) const +{ + return m_tests[ testIndex ].m_path; +} + + +int +ClockerModel::indexOf( CppUnit::Test *test ) const +{ + TestToIndexes::const_iterator itFound = m_testToIndexes.find( test ); + if ( itFound != m_testToIndexes.end() ) + return itFound->second; + return -1; +} + + +int +ClockerModel::childCountFor( int testIndex ) const +{ + return m_tests[ testIndex ].m_childIndexes.size(); +} + + +int +ClockerModel::childAtFor( int testIndex, + int chidIndex ) const +{ + return m_tests[ testIndex ].m_childIndexes[ chidIndex ]; +} diff --git a/examples/ClockerPlugIn/ClockerModel.h b/examples/ClockerPlugIn/ClockerModel.h new file mode 100644 index 0000000..c61a4b7 --- /dev/null +++ b/examples/ClockerPlugIn/ClockerModel.h @@ -0,0 +1,95 @@ +// ////////////////////////////////////////////////////////////////////////// +// Header file ClockerModel.h for class ClockerModel +// (c)Copyright 2000, Baptiste Lepilleur. +// Created: 2002/06/14 +// ////////////////////////////////////////////////////////////////////////// +#ifndef CLOCKERMODEL_H +#define CLOCKERMODEL_H + +#include <cppunit/TestPath.h> +#include <map> +#include <stack> +#include <string> +#include <vector> + +#ifdef CLOCKER_USE_WINNTTIMER +#include "WinNtTimer.h" +typedef WinNtTimer Timer; +#else +#include "Timer.h" +#endif + + +/// Model that represents test timing. +class ClockerModel +{ +public: + /*! Constructs a ClockerModel object. + */ + ClockerModel(); + + /// Destructor. + virtual ~ClockerModel(); + + void setExpectedTestCount( int count ); + + void enterTest( CppUnit::Test *test, + bool isSuite ); + + void exitTest( CppUnit::Test *test, + bool isSuite ); + + double totalElapsedTime() const; + + double averageTestCaseTime() const; + + double testTimeFor( CppUnit::Test *test ) const; + + double testTimeFor( int testIndex ) const; + + static std::string timeStringFor( double time ); + + bool isSuite( int testIndex ) const; + + const CppUnit::TestPath &testPathFor( int testIndex ) const; + + // -1 is none + int indexOf( CppUnit::Test *test ) const; + + int childCountFor( int testIndex ) const; + + int childAtFor( int testIndex, + int chidIndex ) const; + +private: + struct TestInfo + { + CppUnit::TestPath m_path; + Timer m_timer; + bool m_isSuite; + std::vector<int> m_childIndexes; + }; + + /// Prevents the use of the copy constructor. + ClockerModel( const ClockerModel &other ); + + /// Prevents the use of the copy operator. + void operator =( const ClockerModel &other ); + +private: + CppUnit::TestPath m_currentPath; + + int m_testCaseCount; + double m_totalTestCaseTime; + + typedef std::map<CppUnit::Test *, int> TestToIndexes; + + TestToIndexes m_testToIndexes; + std::stack<int> m_testIndexes; + std::vector<TestInfo> m_tests; +}; + + + + +#endif // CLOCKERMODEL_H diff --git a/examples/ClockerPlugIn/ClockerPlugIn.cpp b/examples/ClockerPlugIn/ClockerPlugIn.cpp index 8943629..dbdd672 100644 --- a/examples/ClockerPlugIn/ClockerPlugIn.cpp +++ b/examples/ClockerPlugIn/ClockerPlugIn.cpp @@ -1,6 +1,9 @@ #include <cppunit/TestResult.h> +#include <cppunit/XmlOutputter.h> #include <cppunit/plugin/TestPlugIn.h> +#include "ClockerXmlHook.h" #include "ClockerListener.h" +#include "ClockerModel.h" @@ -9,23 +12,29 @@ class ClockerPlugIn : public CppUnitTestPlugIn public: ClockerPlugIn() : m_dumper( NULL ) + , m_model( NULL ) + , m_xmlHook( NULL ) { } ~ClockerPlugIn() { delete m_dumper; + delete m_model; + delete m_xmlHook; } void initialize( CppUnit::TestFactoryRegistry *registry, const CppUnit::Parameters ¶meters ) { - bool flatten = false; - if ( parameters.size() > 0 && parameters[0] == "flat" ) - flatten = true; + bool text = false; + if ( parameters.size() > 0 && parameters[0] == "text" ) + text = true; - m_dumper = new ClockerListener( flatten ); + m_model = new ClockerModel(); + m_dumper = new ClockerListener( m_model, text ); + m_xmlHook = new ClockerXmlHook( m_model ); } @@ -41,12 +50,25 @@ public: } + void addXmlOutputterHooks( CppUnit::XmlOutputter *outputter ) + { + outputter->addHook( m_xmlHook ); + } + + + void removeXmlOutputterHooks() + { + } + + void uninitialize( CppUnit::TestFactoryRegistry *registry ) { } private: ClockerListener *m_dumper; + ClockerModel *m_model; + ClockerXmlHook *m_xmlHook; }; diff --git a/examples/ClockerPlugIn/ClockerPlugIn.dsp b/examples/ClockerPlugIn/ClockerPlugIn.dsp index 98e206a..8d865e1 100644 --- a/examples/ClockerPlugIn/ClockerPlugIn.dsp +++ b/examples/ClockerPlugIn/ClockerPlugIn.dsp @@ -44,7 +44,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "CLOCKERPLUGIN_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MDd /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "CPPUNIT_DLL" /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\include" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "CPPUNIT_DLL" /FD /c # SUBTRACT CPP /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 @@ -130,14 +130,34 @@ SOURCE=.\ClockerListener.h # End Source File # Begin Source File +SOURCE=.\ClockerModel.cpp +# End Source File +# Begin Source File + +SOURCE=.\ClockerModel.h +# End Source File +# Begin Source File + SOURCE=.\ClockerPlugIn.cpp # End Source File # Begin Source File +SOURCE=.\ClockerXmlHook.cpp +# End Source File +# Begin Source File + +SOURCE=.\ClockerXmlHook.h +# End Source File +# Begin Source File + SOURCE=.\Makefile.am # End Source File # Begin Source File +SOURCE=.\ReadMe.txt +# End Source File +# Begin Source File + SOURCE=.\Timer.cpp !IF "$(CFG)" == "ClockerPlugIn - Win32 Release" diff --git a/examples/ClockerPlugIn/ClockerXmlHook.cpp b/examples/ClockerPlugIn/ClockerXmlHook.cpp new file mode 100644 index 0000000..445b868 --- /dev/null +++ b/examples/ClockerPlugIn/ClockerXmlHook.cpp @@ -0,0 +1,94 @@ +// ////////////////////////////////////////////////////////////////////////// +// Implementation file ClockerXmlHook.cpp for class ClockerXmlHook +// (c)Copyright 2000, Baptiste Lepilleur. +// Created: 2002/06/14 +// ////////////////////////////////////////////////////////////////////////// + +#include <cppunit/Test.h> +#include <cppunit/tools/XmlElement.h> +#include "ClockerModel.h" +#include "ClockerXmlHook.h" + + +ClockerXmlHook::ClockerXmlHook( ClockerModel *model ) + : m_model( model ) +{ +} + + +ClockerXmlHook::~ClockerXmlHook() +{ +} + + +void +ClockerXmlHook::endDocument( CppUnit::XmlDocument *document, + CppUnit::XmlElement *rootNode ) +{ + CppUnit::XmlElement *testTreeElement = new CppUnit::XmlElement( "TimedTestTree" ); + rootNode->addElement( testTreeElement ); + + addTimedTest( testTreeElement, 0 ); +} + + +void +ClockerXmlHook::addTimedTest( CppUnit::XmlElement *parentElement, + int testIndex ) +{ + std::string elementName = m_model->isSuite( testIndex ) ? "TimedSuite" : "TimedTest"; + CppUnit::XmlElement *testElement = new CppUnit::XmlElement( elementName ); + parentElement->addElement( testElement ); + testElement->addAttribute( "id", testIndex ); + + const CppUnit::TestPath &path = m_model->testPathFor( testIndex ); + testElement->addElement( new CppUnit::XmlElement( "Name", + path.getChildTest()->getName() ) ); + testElement->addElement( new CppUnit::XmlElement( "TestPath", path.toString() ) ); + testElement->addElement( new CppUnit::XmlElement( "Time", + ClockerModel::timeStringFor( + m_model->testTimeFor( testIndex ) ) ) ); + + if ( m_model->isSuite( testIndex ) ) + { + for ( int childIndex =0; childIndex < m_model->childCountFor( testIndex ); ++childIndex ) + addTimedTest( testElement, m_model->childAtFor( testIndex, childIndex ) ); + } +} + + +void +ClockerXmlHook::failTestAdded( CppUnit::XmlDocument *document, + CppUnit::XmlElement *testNode, + CppUnit::Test *test, + CppUnit::TestFailure *failure ) +{ + successfulTestAdded( document, testNode, test ); +} + + +void +ClockerXmlHook::successfulTestAdded( CppUnit::XmlDocument *document, + CppUnit::XmlElement *testNode, + CppUnit::Test *test ) +{ + int testIndex = m_model->indexOf( test ); + double time = (testIndex >= 0) ? m_model->testTimeFor( testIndex ) : 0.0; + const CppUnit::TestPath &path = m_model->testPathFor( testIndex ); + testNode->addElement( new CppUnit::XmlElement( "TestPath", path.toString() ) ); + testNode->addElement( new CppUnit::XmlElement( "Time", + ClockerModel::timeStringFor( time ) ) ); +} + + +void +ClockerXmlHook::statisticsAdded( CppUnit::XmlDocument *document, + CppUnit::XmlElement *statisticsNode ) +{ + statisticsNode->addElement( + new CppUnit::XmlElement( "TotalElapsedTime", + ClockerModel::timeStringFor( m_model->totalElapsedTime() ) ) ); + statisticsNode->addElement( + new CppUnit::XmlElement( "AverageTestCaseTime", + ClockerModel::timeStringFor( m_model->averageTestCaseTime() ) ) ); +} diff --git a/examples/ClockerPlugIn/ClockerXmlHook.h b/examples/ClockerPlugIn/ClockerXmlHook.h new file mode 100644 index 0000000..fa86245 --- /dev/null +++ b/examples/ClockerPlugIn/ClockerXmlHook.h @@ -0,0 +1,62 @@ +// ////////////////////////////////////////////////////////////////////////// +// Header file ClockerXmlHook.h for class ClockerXmlHook +// (c)Copyright 2000, Baptiste Lepilleur. +// Created: 2002/06/14 +// ////////////////////////////////////////////////////////////////////////// +#ifndef CLOCKERXMLHOOK_H +#define CLOCKERXMLHOOK_H + +#include <cppunit/XmlOutputterHook.h> + +class ClockerModel; + + + +/// XML output hook to add test timing and test hierarchy timing. +class ClockerXmlHook : public CppUnit::XmlOutputterHook +{ +public: + /*! Constructs a ClockerXmlHook object. + */ + ClockerXmlHook( ClockerModel *model ); + + /// Destructor. + virtual ~ClockerXmlHook(); + + void endDocument( CppUnit::XmlDocument *document, + CppUnit::XmlElement *rootNode ); + + void failTestAdded( CppUnit::XmlDocument *document, + CppUnit::XmlElement *testNode, + CppUnit::Test *test, + CppUnit::TestFailure *failure ); + + void successfulTestAdded( CppUnit::XmlDocument *document, + CppUnit::XmlElement *testNode, + CppUnit::Test *test ); + + void statisticsAdded( CppUnit::XmlDocument *document, + CppUnit::XmlElement *statisticsNode ); + +private: + /// Prevents the use of the copy constructor. + ClockerXmlHook( const ClockerXmlHook &other ); + + /// Prevents the use of the copy operator. + void operator =( const ClockerXmlHook &other ); + + void addTimedTest( CppUnit::XmlElement *parentElement, + int testIndex ); + +private: + ClockerModel *m_model; +}; + + + +// Inlines methods for ClockerXmlHook: +// ----------------------------------- + + + +#endif // CLOCKERXMLHOOK_H diff --git a/examples/ClockerPlugIn/ReadMe.txt b/examples/ClockerPlugIn/ReadMe.txt index 6893fc6..57e4fc5 100644 --- a/examples/ClockerPlugIn/ReadMe.txt +++ b/examples/ClockerPlugIn/ReadMe.txt @@ -1,14 +1,44 @@ -A plug-ins that track tests and test suites running time. A demonstration of TestListener and -test plug-in. +A test plug-ins that track tests and test suites running time. It demonstrates +TestListener, TestPlugIn, and XmlOutputterHook. -Don't use this to profile your application, use a Profiler. +Both suite and test case times are tracked. The plug-in include in the XML +output the TestPath of each test cases and its tracked time. -Usage: +The timed test hierarchy is also included in the XML output. This way it is +possible to see the time each suite takes to run. -The plug-in accept the option "flat" to generate a flattened view of the test tree. + + +* Usage: + +Just add this plug-in to DllPlugInTester command line. It will add a test +listener to track test time, and add a hook to the XmlOutputter to include +test time to the XmlOutput. + +If the option "text" is passed to the plug-in, the timed test tree will be +printed to stdout. DllPlugInRunnerd.exe ClockerPlugInd.dll or -DllPlugInRunnerd.exe ClockerPlugInd.dll=flat +DllPlugInRunnerd.exe ClockerPlugInd.dll=text + +* Example: + +DllPlugInTesterd_dll.exe -x timed.xml ClockerPlugInd.dll CppUnitTestPlugInd.dll + +Will track time of all tests contains in CppUnitTestPlugInd.dll and save the +result in timed.xml. + +* Notes: + +The id of the <TimedTestTree> are different than those of the +<SuccessfulTests> and <FailedTests> trees. You can use the <TestPath> to +cross-reference the datas. + +* Remarks: +You may want to review ClockerModel before using this plug-in for serious +purpose, add timing based on the process cpu time. +A version is provided for NT that use the main thread cpu time. This is an issue +if the test cases are multithreaded. diff --git a/examples/DumperPlugIn/DumperPlugIn.cpp b/examples/DumperPlugIn/DumperPlugIn.cpp index 0131d14..f7b545e 100644 --- a/examples/DumperPlugIn/DumperPlugIn.cpp +++ b/examples/DumperPlugIn/DumperPlugIn.cpp @@ -41,6 +41,16 @@ public: } + void addXmlOutputterHooks( CppUnit::XmlOutputter *outputter ) + { + } + + + void removeXmlOutputterHooks() + { + } + + void uninitialize( CppUnit::TestFactoryRegistry *registry ) { } diff --git a/examples/ReadMe.txt b/examples/ReadMe.txt index 5359a0f..1c2f576 100644 --- a/examples/ReadMe.txt +++ b/examples/ReadMe.txt @@ -11,7 +11,8 @@ cppunittest/: CppUnit's unit tests. Contains CppUnitTestMain which build an appl with CppUnit's tests, and which wrap the same unit tests into a test plug-in. ClockerPlugIn/: a 'TestListener' plug-in. Demonstrates the use of the test plug-in to -extends DllPlugInRunner. The test plug-in tracks the time each test and suite takes to run. +extends DllPlugInRunner. The test plug-in tracks the time each test and suite takes to run +and includes the timing in the XML output. DumperPlugIn/: a 'TestListener' plug-in that dumps the test hierarchy as a tree or in a flattened format (using TestPath). diff --git a/examples/cppunittest/XmlElementTest.cpp b/examples/cppunittest/XmlElementTest.cpp index e460458..26a0778 100644 --- a/examples/cppunittest/XmlElementTest.cpp +++ b/examples/cppunittest/XmlElementTest.cpp @@ -31,6 +31,116 @@ XmlElementTest::tearDown() void +XmlElementTest::testStringContentConstructor() +{ + CppUnit::XmlElement element( "aName", "someContent" ); + CPPUNIT_ASSERT_EQUAL( std::string("aName"), element.name() ); + CPPUNIT_ASSERT_EQUAL( std::string("someContent"), element.content() ); +} + + +void +XmlElementTest::testNumericContentConstructor() +{ + CppUnit::XmlElement element( "numericName", 123456789 ); + CPPUNIT_ASSERT_EQUAL( std::string("numericName"), element.name() ); + CPPUNIT_ASSERT_EQUAL( std::string("123456789"), element.content() ); +} + + +void +XmlElementTest::testSetName() +{ + CppUnit::XmlElement element( "aName" ); + element.setName( "anotherName" ); + CPPUNIT_ASSERT_EQUAL( std::string("anotherName"), element.name() ); +} + + +void +XmlElementTest::testSetStringContent() +{ + CppUnit::XmlElement element( "aName", "someContent" ); + element.setContent( "other" ); + CPPUNIT_ASSERT_EQUAL( std::string("other"), element.content() ); +} + + +void +XmlElementTest::testSetNumericContent() +{ + CppUnit::XmlElement element( "aName", "someContent" ); + element.setContent( 87654321 ); + CPPUNIT_ASSERT_EQUAL( std::string("87654321"), element.content() ); +} + + +void +XmlElementTest::testNodeCount() +{ + CppUnit::XmlElement node( "element", "content" ); + CPPUNIT_ASSERT_EQUAL( 0, node.elementCount() ); + + node.addElement( new CppUnit::XmlElement( "child1" ) ); + node.addElement( new CppUnit::XmlElement( "child2" ) ); + CPPUNIT_ASSERT_EQUAL( 2, node.elementCount() ); +} + + +void +XmlElementTest::testElementAtNegativeIndexThrow() +{ + CppUnit::XmlElement node( "element" ); + node.elementAt( -1 ); +} + + +void +XmlElementTest::testElementAtTooLargeIndexThrow() +{ + CppUnit::XmlElement node( "element" ); + node.elementAt( 0 ); +} + + +void +XmlElementTest::testElementAt() +{ + CppUnit::XmlElement node( "element" ); + CppUnit::XmlElement *element1 = new CppUnit::XmlElement( "element1" ); + CppUnit::XmlElement *element2 = new CppUnit::XmlElement( "element2" ); + node.addElement( element1 ); + node.addElement( element2 ); + + CPPUNIT_ASSERT( element1 == node.elementAt(0) ); + CPPUNIT_ASSERT( element2 == node.elementAt(1) ); +} + + +void +XmlElementTest::testElementForThrow() +{ + CppUnit::XmlElement node( "element" ); + node.addElement( new CppUnit::XmlElement( "element1" ) ); + node.elementFor( "name2" ); +} + + +void +XmlElementTest::testElementFor() +{ + CppUnit::XmlElement node( "element" ); + CppUnit::XmlElement *element1 = new CppUnit::XmlElement( "element1" ); + CppUnit::XmlElement *element2 = new CppUnit::XmlElement( "element2" ); + node.addElement( element1 ); + node.addElement( element2 ); + + CPPUNIT_ASSERT( element2 == node.elementFor( "element2" ) ); + CPPUNIT_ASSERT( element1 == node.elementFor( "element1" ) ); +} + + +void XmlElementTest::testEmptyNodeToString() { CppUnit::XmlElement node( "element" ); @@ -79,8 +189,8 @@ void XmlElementTest::testNodeWithChildrenToString() { CppUnit::XmlElement node( "element" ); - node.addNode( new CppUnit::XmlElement( "child1" ) ); - node.addNode( new CppUnit::XmlElement( "child2" ) ); + node.addElement( new CppUnit::XmlElement( "child1" ) ); + node.addElement( new CppUnit::XmlElement( "child2" ) ); std::string expectedXml = "<element><child1></child1>" "<child2></child2></element>"; CPPUNITTEST_ASSERT_XML_EQUAL( expectedXml, node.toString() ); @@ -109,7 +219,7 @@ void XmlElementTest::testNodeWithContentAndChildToString() { CppUnit::XmlElement node( "element", "content" ); - node.addNode( new CppUnit::XmlElement( "child1" ) ); + node.addElement( new CppUnit::XmlElement( "child1" ) ); std::string expectedXml = "<element><child1></child1>content</element>"; CPPUNITTEST_ASSERT_XML_EQUAL( expectedXml, node.toString() ); } diff --git a/examples/cppunittest/XmlElementTest.h b/examples/cppunittest/XmlElementTest.h index 5b24822..e4f4875 100644 --- a/examples/cppunittest/XmlElementTest.h +++ b/examples/cppunittest/XmlElementTest.h @@ -9,6 +9,18 @@ class XmlElementTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE( XmlElementTest ); + CPPUNIT_TEST( testStringContentConstructor ); + CPPUNIT_TEST( testNumericContentConstructor ); + CPPUNIT_TEST( testSetName ); + CPPUNIT_TEST( testSetStringContent ); + CPPUNIT_TEST( testSetNumericContent ); + CPPUNIT_TEST( testNodeCount ); + CPPUNIT_TEST_EXCEPTION( testElementAtNegativeIndexThrow, std::invalid_argument ); + CPPUNIT_TEST_EXCEPTION( testElementAtTooLargeIndexThrow, std::invalid_argument ); + CPPUNIT_TEST( testElementAt ); + CPPUNIT_TEST_EXCEPTION( testElementForThrow, std::invalid_argument ); + CPPUNIT_TEST( testElementFor ); + CPPUNIT_TEST( testEmptyNodeToString ); CPPUNIT_TEST( testNodeWithAttributesToString ); CPPUNIT_TEST( testEscapedAttributeValueToString ); @@ -30,6 +42,18 @@ public: void setUp(); void tearDown(); + void testStringContentConstructor(); + void testNumericContentConstructor(); + void testSetName(); + void testSetStringContent(); + void testSetNumericContent(); + void testNodeCount(); + void testElementAtNegativeIndexThrow(); + void testElementAtTooLargeIndexThrow(); + void testElementAt(); + void testElementForThrow(); + void testElementFor(); + void testEmptyNodeToString(); void testNodeWithAttributesToString(); void testEscapedAttributeValueToString(); diff --git a/examples/cppunittest/XmlOutputterTest.cpp b/examples/cppunittest/XmlOutputterTest.cpp index 6e08d7d..84a1719 100644 --- a/examples/cppunittest/XmlOutputterTest.cpp +++ b/examples/cppunittest/XmlOutputterTest.cpp @@ -1,6 +1,7 @@ #include <cppunit/XmlOutputter.h> #include <cppunit/TestFailure.h> #include <cppunit/XmlOutputter.h> +#include <cppunit/XmlOutputterHook.h> #include "OutputSuite.h" #include "XmlOutputterTest.h" #include "XmlUniformiser.h" @@ -220,6 +221,89 @@ XmlOutputterTest::testWriteXmlResultWithThreeFailureTwoErrorsAndTwoSuccess() } +class XmlOutputterTest::MockHook : public CppUnit::XmlOutputterHook +{ +public: + MockHook( int &beginCalls, + int &endCalls, + int &statisticsCalls, + int &successfulTestCalls, + int &failedTestCalls ) + : m_successfulTestCalls( successfulTestCalls ) + , m_failedTestCalls( failedTestCalls ) + , m_beginCalls( beginCalls ) + , m_endCalls( endCalls ) + , m_statisticsCalls( statisticsCalls ) + { + } + + void beginDocument( CppUnit::XmlDocument *document, + CppUnit::XmlElement *rootNode ) + { + ++m_beginCalls; + } + + void endDocument( CppUnit::XmlDocument *document, + CppUnit::XmlElement *rootNode ) + { + ++m_endCalls; + } + + void failTestAdded( CppUnit::XmlDocument *document, + CppUnit::XmlElement *testNode, + CppUnit::Test *test, + CppUnit::TestFailure *failure ) + { + ++m_failedTestCalls; + } + + void successfulTestAdded( CppUnit::XmlDocument *document, + CppUnit::XmlElement *testNode, + CppUnit::Test *test ) + { + ++m_successfulTestCalls; + } + + void statisticsAdded( CppUnit::XmlDocument *document, + CppUnit::XmlElement *statisticsNode ) + { + ++m_statisticsCalls; + } + +private: + int &m_beginCalls; + int &m_endCalls; + int &m_statisticsCalls; + int &m_successfulTestCalls; + int &m_failedTestCalls; +}; + + +void +XmlOutputterTest::testHook() +{ + int begin =0, end =0, statistics =0, successful =0, failed =0; + MockHook hook( begin, end, statistics, successful, failed ); + + addTest( "test1" ); + addTest( "test2" ); + addTest( "test3" ); + addTestFailure( "testfail1", "assertion failed" ); + addTestError( "testerror1", "exception" ); + + CppUnit::OStringStream stream; + CppUnit::XmlOutputter outputter( m_result, stream ); + outputter.addHook( &hook ); + outputter.write(); + + CPPUNIT_ASSERT_EQUAL( 1, begin ); + CPPUNIT_ASSERT_EQUAL( 1, end ); + CPPUNIT_ASSERT_EQUAL( 1, statistics ); + CPPUNIT_ASSERT_EQUAL( 3, successful ); + CPPUNIT_ASSERT_EQUAL( 2, failed ); +} + + void XmlOutputterTest::addTest( std::string testName ) { @@ -270,3 +354,4 @@ XmlOutputterTest::makeDummyTest( std::string testName ) m_dummyTests.push_back( test ); return test; } + diff --git a/examples/cppunittest/XmlOutputterTest.h b/examples/cppunittest/XmlOutputterTest.h index 9f155e2..a1600ee 100644 --- a/examples/cppunittest/XmlOutputterTest.h +++ b/examples/cppunittest/XmlOutputterTest.h @@ -19,6 +19,7 @@ class XmlOutputterTest : public CppUnit::TestFixture CPPUNIT_TEST( testWriteXmlResultWithOneError ); CPPUNIT_TEST( testWriteXmlResultWithOneSuccess ); CPPUNIT_TEST( testWriteXmlResultWithThreeFailureTwoErrorsAndTwoSuccess ); + CPPUNIT_TEST( testHook ); CPPUNIT_TEST_SUITE_END(); public: @@ -37,7 +38,11 @@ public: void testWriteXmlResultWithOneSuccess(); void testWriteXmlResultWithThreeFailureTwoErrorsAndTwoSuccess(); + void testHook(); + private: + class MockHook; + /// Prevents the use of the copy constructor. XmlOutputterTest( const XmlOutputterTest © ); diff --git a/examples/examples.opt b/examples/examples.opt Binary files differindex 1cc6969..9fe4d1c 100644 --- a/examples/examples.opt +++ b/examples/examples.opt diff --git a/include/cppunit/Makefile.am b/include/cppunit/Makefile.am index bd505ad..3c7930a 100644 --- a/include/cppunit/Makefile.am +++ b/include/cppunit/Makefile.am @@ -33,7 +33,8 @@ libcppunitinclude_HEADERS = \ TextTestResult.h \ TextTestRunner.h \ TestListener.h \ - XmlOutputter.h + XmlOutputter.h \ + XmlOutputterHook.h dist-hook: rm -f $(distdir)/config-auto.h diff --git a/include/cppunit/XmlOutputter.h b/include/cppunit/XmlOutputter.h index 3336405..dc16913 100644 --- a/include/cppunit/XmlOutputter.h +++ b/include/cppunit/XmlOutputter.h @@ -9,6 +9,7 @@ #endif #include <cppunit/Outputter.h> +#include <deque> #include <iostream> #include <map> @@ -21,10 +22,19 @@ class TestFailure; class TestResultCollector; class XmlDocument; class XmlElement; +class XmlOutputterHook; /*! \brief Outputs a TestResultCollector in XML format. * \ingroup WritingTestResult + * + * Save the test result as a XML stream. + * + * Additional datas can be added to the XML document using XmlOutputterHook. + * Hook are not owned by the XmlOutputter. They should be valid until + * destruction of the XmlOutputter. They can be removed with removeHook(). + * + * \see XmlDocument, XmlElement, XmlOutputterHook. */ class CPPUNIT_API XmlOutputter : public Outputter { @@ -41,6 +51,16 @@ public: /// Destructor. virtual ~XmlOutputter(); + /*! Adds the specified hook to the outputter. + * \param hook Hook to add. Must not be \c NULL. + */ + virtual void addHook( XmlOutputterHook *hook ); + + /*! Removes the specified hook from the outputter. + * \param hook Hook to remove. + */ + virtual void removeHook( XmlOutputterHook *hook ); + /*! Writes the specified result as an XML document to the stream. * * Refer to examples/cppunittest/XmlOutputterTest.cpp for example @@ -57,18 +77,48 @@ public: typedef std::map<Test *,TestFailure*> FailedTests; + + /*! Returns the root element with its children. + * + * For all hooks, call beginDocument() just after creating the root element (it + * is empty at this time), and endDocument() once all the datas have been added + * to the root element. + * + * \return Root element. + */ virtual XmlElement *makeRootNode(); virtual void addFailedTests( FailedTests &failedTests, XmlElement *rootNode ); virtual void addSuccessfulTests( FailedTests &failedTests, XmlElement *rootNode ); + + /*! Adds the statics element to the root node. + * + * Creates a new element containing statistics data and adds it to the root element. + * Then, for all hooks, call statisticsAdded(). + * \param rootNode Root element. + */ virtual void addStatistics( XmlElement *rootNode ); + + /*! Adds a failed test to the failed tests node. + * Creates a new element containing datas about the failed test, and adds it to + * the failed tests element. + * Then, for all hooks, call failTestAdded(). + */ virtual void addFailedTest( Test *test, TestFailure *failure, int testNumber, XmlElement *testsNode ); + virtual void addFailureLocation( TestFailure *failure, XmlElement *testNode ); + + + /*! Adds a successful test to the successful tests node. + * Creates a new element containing datas about the successful test, and adds it to + * the successful tests element. + * Then, for all hooks, call successfulTestAdded(). + */ virtual void addSuccessfulTest( Test *test, int testNumber, XmlElement *testsNode ); @@ -76,11 +126,14 @@ protected: virtual void fillFailedTestsMap( FailedTests &failedTests ); protected: + typedef std::deque<XmlOutputterHook *> Hooks; + TestResultCollector *m_result; std::ostream &m_stream; std::string m_encoding; std::string m_styleSheet; XmlDocument *m_xml; + Hooks m_hooks; private: /// Prevents the use of the copy constructor. diff --git a/include/cppunit/XmlOutputterHook.h b/include/cppunit/XmlOutputterHook.h new file mode 100644 index 0000000..b67c6cf --- /dev/null +++ b/include/cppunit/XmlOutputterHook.h @@ -0,0 +1,46 @@ +#ifndef CPPUNIT_XMLOUTPUTTERHOOK_H +#define CPPUNIT_XMLOUTPUTTERHOOK_H + +#include <cppunit/Portability.h> + + +namespace CppUnit +{ + +class Test; +class TestFailure; +class XmlDocument; +class XmlElement; + + + +/*! \brief Hook to customize Xml output. + */ +class CPPUNIT_API XmlOutputterHook +{ +public: + virtual void beginDocument( XmlDocument *document, + XmlElement *rootNode ); + + virtual void endDocument( XmlDocument *document, + XmlElement *rootNode ); + + virtual void failTestAdded( XmlDocument *document, + XmlElement *testNode, + Test *test, + TestFailure *failure ); + + virtual void successfulTestAdded( XmlDocument *document, + XmlElement *testNode, + Test *test ); + + virtual void statisticsAdded( XmlDocument *document, + XmlElement *statisticsNode ); +}; + + + +} // namespace CppUnit + + +#endif // CPPUNIT_XMLOUTPUTTERHOOK_H diff --git a/include/cppunit/plugin/Makefile.am b/include/cppunit/plugin/Makefile.am index 0494373..3fdff64 100644 --- a/include/cppunit/plugin/Makefile.am +++ b/include/cppunit/plugin/Makefile.am @@ -4,6 +4,6 @@ libcppunitinclude_HEADERS = \ DynamicLibraryManager.h \ DynamicLibraryManagerException.h \ TestPlugIn.h \ - TestPlugInAdapter.h \ + TestPlugInDefaultImpl.h \ PlugInManager.h \ Parameters.h diff --git a/include/cppunit/plugin/PlugInManager.h b/include/cppunit/plugin/PlugInManager.h index d3bbc5f..f349ab1 100644 --- a/include/cppunit/plugin/PlugInManager.h +++ b/include/cppunit/plugin/PlugInManager.h @@ -18,6 +18,7 @@ namespace CppUnit class DynamicLibraryManager; class TestResult; +class XmlOutputter; /*! \brief Manges TestPlugIn. @@ -62,6 +63,16 @@ public: */ void removeListener( TestResult *eventManager ); + /*! Provides a way for the plug-in to register some XmlOutputterHook. + */ + void addXmlOutputterHooks( XmlOutputter *outputter ); + + /*! Called when the XmlOutputter is destroyed. + * + * Can be used to free some resources allocated by addXmlOutputterHooks(). + */ + void removeXmlOutputterHooks(); + protected: struct PlugInInfo { diff --git a/include/cppunit/plugin/TestPlugIn.h b/include/cppunit/plugin/TestPlugIn.h index 0847928..0eaf960 100644 --- a/include/cppunit/plugin/TestPlugIn.h +++ b/include/cppunit/plugin/TestPlugIn.h @@ -12,6 +12,7 @@ namespace CppUnit class Test; class TestFactoryRegistry; class TestResult; +class XmlOutputter; } /*! \file @@ -67,6 +68,16 @@ struct CppUnitTestPlugIn */ virtual void removeListener( CppUnit::TestResult *eventManager ) =0; + /*! Provides a way for the plug-in to register some XmlOutputterHook. + */ + virtual void addXmlOutputterHooks( CppUnit::XmlOutputter *outputter ) =0; + + /*! Called when the XmlOutputter is destroyed. + * + * Can be used to free some resources allocated by addXmlOutputterHooks(). + */ + virtual void removeXmlOutputterHooks() = 0; + /*! Called just before unloading the dynamic library. * * Override this method to unregister test factory added in initialize(). @@ -109,7 +120,7 @@ typedef CppUnitTestPlugIn *(*TestPlugInSignature)(); // Note: This include should remain after definition of CppUnitTestPlugIn -#include <cppunit/plugin/TestPlugInAdapter.h> +#include <cppunit/plugin/TestPlugInDefaultImpl.h> /*! \def CPPUNIT_PLUGIN_IMPLEMENT_MAIN() @@ -170,8 +181,8 @@ typedef CppUnitTestPlugIn *(*TestPlugInSignature)(); * \see CppUnitTestPlugIn * \see CPPUNIT_PLUGIN_EXPORTED_FUNCTION_IMPL(), CPPUNIT_PLUGIN_IMPLEMENT_MAIN(). */ -#define CPPUNIT_PLUGIN_IMPLEMENT() \ - CPPUNIT_PLUGIN_EXPORTED_FUNCTION_IMPL( CppUnit::TestPlugInAdapter ); \ +#define CPPUNIT_PLUGIN_IMPLEMENT() \ + CPPUNIT_PLUGIN_EXPORTED_FUNCTION_IMPL( CppUnit::TestPlugInDefaultImpl ); \ CPPUNIT_PLUGIN_IMPLEMENT_MAIN() diff --git a/include/cppunit/plugin/TestPlugInAdapter.h b/include/cppunit/plugin/TestPlugInDefaultImpl.h index 0e54e0e..90d4398 100644 --- a/include/cppunit/plugin/TestPlugInAdapter.h +++ b/include/cppunit/plugin/TestPlugInDefaultImpl.h @@ -23,12 +23,12 @@ class TestSuite; * ( TestFactoryRegistry::getRegistry() ). * */ -class CPPUNIT_API TestPlugInAdapter : public CppUnitTestPlugIn +class CPPUNIT_API TestPlugInDefaultImpl : public CppUnitTestPlugIn { public: - TestPlugInAdapter(); + TestPlugInDefaultImpl(); - virtual ~TestPlugInAdapter(); + virtual ~TestPlugInDefaultImpl(); void initialize( TestFactoryRegistry *registry, const Parameters ¶meters ); @@ -37,6 +37,10 @@ public: void removeListener( TestResult *eventManager ); + void addXmlOutputterHooks( XmlOutputter *outputter ); + + void removeXmlOutputterHooks(); + void uninitialize( TestFactoryRegistry *registry ); }; diff --git a/include/cppunit/tools/Makefile.am b/include/cppunit/tools/Makefile.am index 7035ef7..d06fdb3 100644 --- a/include/cppunit/tools/Makefile.am +++ b/include/cppunit/tools/Makefile.am @@ -1,5 +1,6 @@ libcppunitincludedir = $(includedir)/cppunit/tools libcppunitinclude_HEADERS = \ + StringTools.h \ XmlElement.h \ XmlDocument.h
\ No newline at end of file diff --git a/include/cppunit/tools/StringTools.h b/include/cppunit/tools/StringTools.h new file mode 100644 index 0000000..8242c9e --- /dev/null +++ b/include/cppunit/tools/StringTools.h @@ -0,0 +1,27 @@ +#ifndef CPPUNIT_TOOLS_STRINGTOOLS_H +#define CPPUNIT_TOOLS_STRINGTOOLS_H + +#include <cppunit/Portability.h> +#include <string> + + +namespace CppUnit +{ + +/*! \brief Tool functions to manipulate string. + */ +namespace StringTools +{ + + std::string CPPUNIT_API toString( int value ); + + std::string CPPUNIT_API toString( double value ); + + +} // namespace StringTools + + +} // namespace CppUnit + + +#endif // CPPUNIT_TOOLS_STRINGTOOLS_H diff --git a/include/cppunit/tools/XmlElement.h b/include/cppunit/tools/XmlElement.h index 323642e..760002d 100644 --- a/include/cppunit/tools/XmlElement.h +++ b/include/cppunit/tools/XmlElement.h @@ -22,23 +22,103 @@ class XmlElement; #endif -/*! A XML Element. +/*! \brief A XML Element. + * + * A XML element has: + * - a name, specified on construction, + * - a content, specified on construction (may be empty), + * - zero or more attributes, added with addAttribute(), + * - zero or more child elements, added with addElement(). */ class CPPUNIT_API XmlElement { public: + /*! \brief Constructs an element with the specified name and string content. + * \param elementName Name of the element. Must not be empty. + * \param content Content of the element. + */ XmlElement( std::string elementName, std::string content ="" ); + + /*! \brief Constructs an element with the specified name and numeric content. + * \param elementName Name of the element. Must not be empty. + * \param numericContent Content of the element. + */ XmlElement( std::string elementName, int numericContent ); + + /*! \brief Destructs the element and its child elements. + */ virtual ~XmlElement(); + /*! \brief Returns the name of the element. + * \return Name of the element. + */ + std::string name() const; + + /*! \brief Returns the content of the element. + * \return Content of the element. + */ + std::string content() const; + + /*! \brief Sets the name of the element. + * \param name New name for the element. + */ + void setName( const std::string &name ); + + /*! \brief Sets the content of the element. + * \param content New content for the element. + */ + void setContent( const std::string &content ); + + /*! \overload. + */ + void setContent( int numericContent ); + + /*! \brief Adds an attribute with the specified string value. + * \param attributeName Name of the attribute. Must not be an empty. + * \param value Value of the attribute. + */ void addAttribute( std::string attributeName, std::string value ); + + /*! \brief Adds an attribute with the specified numeric value. + * \param attributeName Name of the attribute. Must not be empty. + * \param value Numeric value of the attribute. + */ void addAttribute( std::string attributeName, int numericValue ); - void addNode( XmlElement *element ); + /*! \brief Adds a child element to the element. + * \param element Child element to add. Must not be \c NULL. + */ + void addElement( XmlElement *element ); + + /*! \brief Returns the number of child elements. + * \return Number of child elements (element added with addElement()). + */ + int elementCount() const; + + /*! \brief Returns the child element at the specified index. + * \param index Zero based index of the element to return. + * \returns Element at the specified index. Never \c NULL. + * \exception std::invalid_argument if \a index < 0 or index >= elementCount(). + */ + XmlElement *elementAt( int index ) const; + + /*! \brief Returns the first child element with the specified name. + * \param name Name of the child element to return. + * \return First child element found which is named \a name. + * \exception std::invalid_argument if there is no child element with the specified + * name. + */ + XmlElement *elementFor( const std::string &name ) const; + + /*! \brief Returns a XML string that represents the element. + * \param indent String of spaces representing the amount of 'indent'. + * \return XML string that represents the element, its attributes and its + * child elements. + */ std::string toString( const std::string &indent = "" ) const; private: @@ -46,7 +126,6 @@ private: std::string attributesAsString() const; std::string escape( std::string value ) const; - static std::string asString( int value ); private: std::string m_name; diff --git a/src/DllPlugInTester/DllPlugInTester.cpp b/src/DllPlugInTester/DllPlugInTester.cpp index b00fb77..3bbb797 100644 --- a/src/DllPlugInTester/DllPlugInTester.cpp +++ b/src/DllPlugInTester/DllPlugInTester.cpp @@ -17,6 +17,13 @@ #include "CommandLineParser.h" +/* Notes: + + Memory allocated by test plug-in must be freed before unloading the test plug-in. + That is the reason why the XmlOutputter is explicitely destroyed. + */ + + /*! Runs the specified tests located in the root suite. * \param parser Command line parser. * \return \c true if the run succeed, \c false if a test failed or if a test @@ -25,76 +32,85 @@ bool runTests( const CommandLineParser &parser ) { - CppUnit::TestResult controller; - CppUnit::TestResultCollector result; - controller.addListener( &result ); - - // Set up outputters - std::ostream *stream = &std::cerr; - if ( parser.useCoutStream() ) - stream = &std::cout; - - std::ostream *xmlStream = stream; - if ( !parser.getXmlFileName().empty() ) - xmlStream = new std::ofstream( parser.getXmlFileName().c_str() ); - - CppUnit::XmlOutputter xmlOutputter( &result, *xmlStream, parser.getEncoding() ); - xmlOutputter.setStyleSheet( parser.getXmlStyleSheet() ); - CppUnit::TextOutputter textOutputter( &result, *stream ); - CppUnit::CompilerOutputter compilerOutputter( &result, *stream ); - - // Set up test listeners - CppUnit::BriefTestProgressListener briefListener; - CppUnit::TextTestProgressListener dotListener; - if ( parser.useBriefTestProgress() ) - controller.addListener( &briefListener ); - else if ( !parser.noTestProgress() ) - controller.addListener( &dotListener ); - - // Set up plug-ins + bool wasSuccessful = false; CppUnit::PlugInManager plugInManager; - for ( int index =0; index < parser.getPlugInCount(); ++index ) + + // The following scope is used to explicitely free all memory allocated before + // unload the test plug-ins (uppon plugInManager destruction). { - CommandLinePlugInInfo plugIn = parser.getPlugInAt( index ); - plugInManager.load( plugIn.m_fileName, plugIn.m_parameters ); - } + CppUnit::TestResult controller; + CppUnit::TestResultCollector result; + controller.addListener( &result ); - // Registers plug-in specific TestListener (global setUp/tearDown, custom TestListener...) - plugInManager.addListener( &controller ); + // Set up outputters + std::ostream *stream = &std::cerr; + if ( parser.useCoutStream() ) + stream = &std::cout; - // Adds the default registry suite - CppUnit::TestRunner runner; - runner.addTest( CppUnit::TestFactoryRegistry::getRegistry().makeTest() ); + std::ostream *xmlStream = stream; + if ( !parser.getXmlFileName().empty() ) + xmlStream = new std::ofstream( parser.getXmlFileName().c_str() ); - // Runs the specified test - bool wasSuccessful = false; - try - { - runner.run( controller, parser.getTestPath() ); - wasSuccessful = result.wasSuccessful(); - } - catch ( std::invalid_argument & ) - { - std::cerr << "Failed to resolve test path: " - << parser.getTestPath() - << std::endl; - } + CppUnit::XmlOutputter xmlOutputter( &result, *xmlStream, parser.getEncoding() ); + xmlOutputter.setStyleSheet( parser.getXmlStyleSheet() ); + CppUnit::TextOutputter textOutputter( &result, *stream ); + CppUnit::CompilerOutputter compilerOutputter( &result, *stream ); + + // Set up test listeners + CppUnit::BriefTestProgressListener briefListener; + CppUnit::TextTestProgressListener dotListener; + if ( parser.useBriefTestProgress() ) + controller.addListener( &briefListener ); + else if ( !parser.noTestProgress() ) + controller.addListener( &dotListener ); + + // Set up plug-ins + for ( int index =0; index < parser.getPlugInCount(); ++index ) + { + CommandLinePlugInInfo plugIn = parser.getPlugInAt( index ); + plugInManager.load( plugIn.m_fileName, plugIn.m_parameters ); + } - // Removes plug-in specific TestListener (not really needed but...) - plugInManager.removeListener( &controller ); + // Registers plug-in specific TestListener (global setUp/tearDown, custom TestListener...) + plugInManager.addListener( &controller ); - // write using outputters - if ( parser.useCompilerOutputter() ) - compilerOutputter.write(); + // Adds the default registry suite + CppUnit::TestRunner runner; + runner.addTest( CppUnit::TestFactoryRegistry::getRegistry().makeTest() ); - if ( parser.useTextOutputter() ) - textOutputter.write(); + // Runs the specified test + try + { + runner.run( controller, parser.getTestPath() ); + wasSuccessful = result.wasSuccessful(); + } + catch ( std::invalid_argument & ) + { + std::cerr << "Failed to resolve test path: " + << parser.getTestPath() + << std::endl; + } - if ( parser.useXmlOutputter() ) - xmlOutputter.write(); + // Removes plug-in specific TestListener (not really needed but...) + plugInManager.removeListener( &controller ); - if ( !parser.getXmlFileName().empty() ) - delete xmlStream; + // write using outputters + if ( parser.useCompilerOutputter() ) + compilerOutputter.write(); + + if ( parser.useTextOutputter() ) + textOutputter.write(); + + if ( parser.useXmlOutputter() ) + { + plugInManager.addXmlOutputterHooks( &xmlOutputter ); + xmlOutputter.write(); + plugInManager.removeXmlOutputterHooks(); + } + + if ( !parser.getXmlFileName().empty() ) + delete xmlStream; + } return wasSuccessful; } diff --git a/src/cppunit/Makefile.am b/src/cppunit/Makefile.am index 80b1888..3846e64 100644 --- a/src/cppunit/Makefile.am +++ b/src/cppunit/Makefile.am @@ -1,5 +1,5 @@ # -# $Id: Makefile.am,v 1.34 2002-06-13 18:17:42 blep Exp $ +# $Id: Makefile.am,v 1.35 2002-06-14 20:21:00 blep Exp $ # EXTRA_DIST = cppunit.dsp cppunit_dll.dsp DllMain.cpp @@ -19,6 +19,7 @@ libcppunit_la_SOURCES = \ RepeatedTest.cpp \ PlugInManager.cpp \ SourceLine.cpp \ + StringTools.cpp \ SynchronizedObject.cpp \ Test.cpp \ TestAssert.cpp \ @@ -29,7 +30,7 @@ libcppunit_la_SOURCES = \ TestLeaf.cpp \ TestNamer.cpp \ TestPath.cpp \ - TestPlugInAdapter.cpp \ + TestPlugInDefaultImpl.cpp \ TestResult.cpp \ TestResultCollector.cpp \ TestRunner.cpp \ @@ -45,6 +46,7 @@ libcppunit_la_SOURCES = \ XmlDocument.cpp \ XmlElement.cpp \ XmlOutputter.cpp \ + XmlOutputterHook.cpp \ Win32DynamicLibraryManager.cpp libcppunit_la_LDFLAGS= \ diff --git a/src/cppunit/PlugInManager.cpp b/src/cppunit/PlugInManager.cpp index f339c63..8f2476d 100644 --- a/src/cppunit/PlugInManager.cpp +++ b/src/cppunit/PlugInManager.cpp @@ -1,3 +1,4 @@ +#include <cppunit/XmlOutputterHook.h> #include <cppunit/plugin/PlugInManager.h> #include <cppunit/extensions/TestFactoryRegistry.h> #include <cppunit/plugin/TestPlugIn.h> @@ -87,5 +88,21 @@ PlugInManager::unload( PlugInInfo &plugIn ) } +void +PlugInManager::addXmlOutputterHooks( XmlOutputter *outputter ) +{ + for ( PlugIns::iterator it = m_plugIns.begin(); it != m_plugIns.end(); ++it ) + it->m_interface->addXmlOutputterHooks( outputter ); +} + + +void +PlugInManager::removeXmlOutputterHooks() +{ + for ( PlugIns::iterator it = m_plugIns.begin(); it != m_plugIns.end(); ++it ) + it->m_interface->removeXmlOutputterHooks(); +} + + } // namespace CppUnit diff --git a/src/cppunit/StringTools.cpp b/src/cppunit/StringTools.cpp new file mode 100644 index 0000000..9bfb939 --- /dev/null +++ b/src/cppunit/StringTools.cpp @@ -0,0 +1,29 @@ +#include <cppunit/tools/StringTools.h> + + +namespace CppUnit +{ + +namespace StringTools +{ + + std::string toString( int value ) + { + OStringStream stream; + stream << value; + return stream.str(); + } + + std::string toString( double value ) + { + OStringStream stream; + stream << value; + return stream.str(); + } + + +} // namespace StringTools + + +} // namespace CppUnit + diff --git a/src/cppunit/TestPlugInAdapter.cpp b/src/cppunit/TestPlugInAdapter.cpp deleted file mode 100644 index 87f761a..0000000 --- a/src/cppunit/TestPlugInAdapter.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include <cppunit/Portability.h> - -#if !defined(CPPUNIT_NO_TESTPLUGIN) - -#include <cppunit/extensions/TestFactoryRegistry.h> -#include <cppunit/TestSuite.h> -#include <cppunit/plugin/TestPlugInAdapter.h> - - -namespace CppUnit -{ - -TestPlugInAdapter::TestPlugInAdapter() -{ -} - - -TestPlugInAdapter::~TestPlugInAdapter() -{ -} - - -void -TestPlugInAdapter::initialize( TestFactoryRegistry *registry, - const Parameters ¶meters ) -{ -} - - -void -TestPlugInAdapter::addListener( TestResult *eventManager ) -{ -} - - -void -TestPlugInAdapter::removeListener( TestResult *eventManager ) -{ -} - - -void -TestPlugInAdapter::uninitialize( TestFactoryRegistry *registry ) -{ -} - - -} // namespace CppUnit - - -#endif // !defined(CPPUNIT_NO_TESTPLUGIN) diff --git a/src/cppunit/TestPlugInDefaultImpl.cpp b/src/cppunit/TestPlugInDefaultImpl.cpp new file mode 100644 index 0000000..55010ed --- /dev/null +++ b/src/cppunit/TestPlugInDefaultImpl.cpp @@ -0,0 +1,63 @@ +#include <cppunit/Portability.h> + +#if !defined(CPPUNIT_NO_TESTPLUGIN) + +#include <cppunit/extensions/TestFactoryRegistry.h> +#include <cppunit/TestSuite.h> +#include <cppunit/plugin/TestPlugInDefaultImpl.h> + + +namespace CppUnit +{ + +TestPlugInDefaultImpl::TestPlugInDefaultImpl() +{ +} + + +TestPlugInDefaultImpl::~TestPlugInDefaultImpl() +{ +} + + +void +TestPlugInDefaultImpl::initialize( TestFactoryRegistry *registry, + const Parameters ¶meters ) +{ +} + + +void +TestPlugInDefaultImpl::addListener( TestResult *eventManager ) +{ +} + + +void +TestPlugInDefaultImpl::removeListener( TestResult *eventManager ) +{ +} + + +void +TestPlugInDefaultImpl::addXmlOutputterHooks( XmlOutputter *outputter ) +{ +} + + +void +TestPlugInDefaultImpl::removeXmlOutputterHooks() +{ +} + + +void +TestPlugInDefaultImpl::uninitialize( TestFactoryRegistry *registry ) +{ +} + + +} // namespace CppUnit + + +#endif // !defined(CPPUNIT_NO_TESTPLUGIN) diff --git a/src/cppunit/XmlElement.cpp b/src/cppunit/XmlElement.cpp index 4a01405..be06941 100644 --- a/src/cppunit/XmlElement.cpp +++ b/src/cppunit/XmlElement.cpp @@ -1,3 +1,4 @@ +#include <cppunit/tools/StringTools.h> #include <cppunit/tools/XmlElement.h> @@ -17,7 +18,7 @@ XmlElement::XmlElement( std::string elementName, int numericContent ) : m_name( elementName ) { - m_content = asString( numericContent ); + setContent( numericContent ); } @@ -25,7 +26,45 @@ XmlElement::~XmlElement() { Elements::iterator itNode = m_elements.begin(); while ( itNode != m_elements.end() ) + { + XmlElement *element = *itNode; delete *itNode++; + } +} + + +std::string +XmlElement::name() const +{ + return m_name; +} + + +std::string +XmlElement::content() const +{ + return m_content; +} + + +void +XmlElement::setName( const std::string &name ) +{ + m_name = name; +} + + +void +XmlElement::setContent( const std::string &content ) +{ + m_content = content; +} + + +void +XmlElement::setContent( int numericContent ) +{ + m_content = StringTools::toString( numericContent ); } @@ -41,17 +80,49 @@ void XmlElement::addAttribute( std::string attributeName, int numericValue ) { - addAttribute( attributeName, asString( numericValue ) ); + addAttribute( attributeName, StringTools::toString( numericValue ) ); } void -XmlElement::addNode( XmlElement *node ) +XmlElement::addElement( XmlElement *node ) { m_elements.push_back( node ); } +int +XmlElement::elementCount() const +{ + return m_elements.size(); +} + + +XmlElement * +XmlElement::elementAt( int index ) const +{ + if ( index < 0 || index >= elementCount() ) + throw std::invalid_argument( "XmlElement::elementAt(), out of range index" ); + + return m_elements[ index ]; +} + + +XmlElement * +XmlElement::elementFor( const std::string &name ) const +{ + Elements::const_iterator itElement = m_elements.begin(); + for ( ; itElement != m_elements.end(); ++itElement ) + { + if ( (*itElement)->name() == name ) + return *itElement; + } + + throw std::invalid_argument( "XmlElement::elementFor(), not matching child element found" ); + return NULL; // make some compilers happy. +} + + std::string XmlElement::toString( const std::string &indent ) const { @@ -147,15 +218,6 @@ XmlElement::escape( std::string value ) const return escaped; } -// should be somewhere else... Future CppUnit::String ? -std::string -XmlElement::asString( int value ) -{ - OStringStream stream; - stream << value; - return stream.str(); -} - } // namespace CppUnit diff --git a/src/cppunit/XmlOutputter.cpp b/src/cppunit/XmlOutputter.cpp index 42e6b32..9fc59f7 100644 --- a/src/cppunit/XmlOutputter.cpp +++ b/src/cppunit/XmlOutputter.cpp @@ -3,9 +3,11 @@ #include <cppunit/TestFailure.h> #include <cppunit/TestResultCollector.h> #include <cppunit/XmlOutputter.h> +#include <cppunit/XmlOutputterHook.h> #include <cppunit/tools/XmlDocument.h> #include <cppunit/tools/XmlElement.h> #include <stdlib.h> +#include <algorithm> namespace CppUnit @@ -39,6 +41,20 @@ XmlOutputter::~XmlOutputter() void +XmlOutputter::addHook( XmlOutputterHook *hook ) +{ + m_hooks.push_back( hook ); +} + + +void +XmlOutputter::removeHook( XmlOutputterHook *hook ) +{ + m_hooks.erase( std::find( m_hooks.begin(), m_hooks.end(), hook ) ); +} + + +void XmlOutputter::write() { m_xml->setRootElement( makeRootNode() ); @@ -58,6 +74,9 @@ XmlOutputter::makeRootNode() { XmlElement *rootNode = new XmlElement( "TestRun" ); + for ( Hooks::const_iterator it = m_hooks.begin(); it != m_hooks.end(); ++it ) + (*it)->beginDocument( m_xml, rootNode ); + FailedTests failedTests; fillFailedTestsMap( failedTests ); @@ -65,6 +84,9 @@ XmlOutputter::makeRootNode() addSuccessfulTests( failedTests, rootNode ); addStatistics( rootNode ); + for ( Hooks::const_iterator itEnd = m_hooks.begin(); itEnd != m_hooks.end(); ++itEnd ) + (*itEnd)->endDocument( m_xml, rootNode ); + return rootNode; } @@ -87,7 +109,7 @@ XmlOutputter::addFailedTests( FailedTests &failedTests, XmlElement *rootNode ) { XmlElement *testsNode = new XmlElement( "FailedTests" ); - rootNode->addNode( testsNode ); + rootNode->addElement( testsNode ); const TestResultCollector::Tests &tests = m_result->tests(); for ( int testNumber = 0; testNumber < tests.size(); ++testNumber ) @@ -104,7 +126,7 @@ XmlOutputter::addSuccessfulTests( FailedTests &failedTests, XmlElement *rootNode ) { XmlElement *testsNode = new XmlElement( "SuccessfulTests" ); - rootNode->addNode( testsNode ); + rootNode->addElement( testsNode ); const TestResultCollector::Tests &tests = m_result->tests(); for ( int testNumber = 0; testNumber < tests.size(); ++testNumber ) @@ -120,12 +142,15 @@ void XmlOutputter::addStatistics( XmlElement *rootNode ) { XmlElement *statisticsNode = new XmlElement( "Statistics" ); - rootNode->addNode( statisticsNode ); - statisticsNode->addNode( new XmlElement( "Tests", m_result->runTests() ) ); - statisticsNode->addNode( new XmlElement( "FailuresTotal", - m_result->testFailuresTotal() ) ); - statisticsNode->addNode( new XmlElement( "Errors", m_result->testErrors() ) ); - statisticsNode->addNode( new XmlElement( "Failures", m_result->testFailures() ) ); + rootNode->addElement( statisticsNode ); + statisticsNode->addElement( new XmlElement( "Tests", m_result->runTests() ) ); + statisticsNode->addElement( new XmlElement( "FailuresTotal", + m_result->testFailuresTotal() ) ); + statisticsNode->addElement( new XmlElement( "Errors", m_result->testErrors() ) ); + statisticsNode->addElement( new XmlElement( "Failures", m_result->testFailures() ) ); + + for ( Hooks::const_iterator it = m_hooks.begin(); it != m_hooks.end(); ++it ) + (*it)->statisticsAdded( m_xml, statisticsNode ); } @@ -138,40 +163,47 @@ XmlOutputter::addFailedTest( Test *test, Exception *thrownException = failure->thrownException(); XmlElement *testNode = new XmlElement( "FailedTest" ); - testsNode->addNode( testNode ); + testsNode->addElement( testNode ); testNode->addAttribute( "id", testNumber ); - testNode->addNode( new XmlElement( "Name", test->getName() ) ); - testNode->addNode( new XmlElement( "FailureType", - failure->isError() ? "Error" : "Assertion" ) ); + testNode->addElement( new XmlElement( "Name", test->getName() ) ); + testNode->addElement( new XmlElement( "FailureType", + failure->isError() ? "Error" : + "Assertion" ) ); if ( failure->sourceLine().isValid() ) addFailureLocation( failure, testNode ); - testNode->addNode( new XmlElement( "Message", thrownException->what() ) ); + testNode->addElement( new XmlElement( "Message", thrownException->what() ) ); + + for ( Hooks::const_iterator it = m_hooks.begin(); it != m_hooks.end(); ++it ) + (*it)->failTestAdded( m_xml, testNode, test, failure ); } void XmlOutputter::addFailureLocation( TestFailure *failure, - XmlElement *testNode ) + XmlElement *testNode ) { XmlElement *locationNode = new XmlElement( "Location" ); - testNode->addNode( locationNode ); + testNode->addElement( locationNode ); SourceLine sourceLine = failure->sourceLine(); - locationNode->addNode( new XmlElement( "File", sourceLine.fileName() ) ); - locationNode->addNode( new XmlElement( "Line", sourceLine.lineNumber() ) ); + locationNode->addElement( new XmlElement( "File", sourceLine.fileName() ) ); + locationNode->addElement( new XmlElement( "Line", sourceLine.lineNumber() ) ); } void XmlOutputter::addSuccessfulTest( Test *test, - int testNumber, - XmlElement *testsNode ) + int testNumber, + XmlElement *testsNode ) { XmlElement *testNode = new XmlElement( "Test" ); - testsNode->addNode( testNode ); + testsNode->addElement( testNode ); testNode->addAttribute( "id", testNumber ); - testNode->addNode( new XmlElement( "Name", test->getName() ) ); + testNode->addElement( new XmlElement( "Name", test->getName() ) ); + + for ( Hooks::const_iterator it = m_hooks.begin(); it != m_hooks.end(); ++it ) + (*it)->successfulTestAdded( m_xml, testNode, test ); } diff --git a/src/cppunit/XmlOutputterHook.cpp b/src/cppunit/XmlOutputterHook.cpp new file mode 100644 index 0000000..4cacd06 --- /dev/null +++ b/src/cppunit/XmlOutputterHook.cpp @@ -0,0 +1,47 @@ +#include <cppunit/XmlOutputterHook.h> + + +namespace CppUnit +{ + + +void +XmlOutputterHook::beginDocument( XmlDocument *document, + XmlElement *rootNode ) +{ +} + + +void +XmlOutputterHook::endDocument( XmlDocument *document, + XmlElement *rootNode ) +{ +} + + +void +XmlOutputterHook::failTestAdded( XmlDocument *document, + XmlElement *testNode, + Test *test, + TestFailure *failure ) +{ +} + + +void +XmlOutputterHook::successfulTestAdded( XmlDocument *document, + XmlElement *testNode, + Test *test ) +{ +} + + +void +XmlOutputterHook::statisticsAdded( XmlDocument *document, + XmlElement *statisticsNode ) +{ +} + + +} // namespace CppUnit + diff --git a/src/cppunit/cppunit.dsp b/src/cppunit/cppunit.dsp index abaa6ba..2111bbd 100644 --- a/src/cppunit/cppunit.dsp +++ b/src/cppunit/cppunit.dsp @@ -231,6 +231,14 @@ SOURCE=.\XmlOutputter.cpp SOURCE=..\..\include\cppunit\XmlOutputter.h # End Source File +# Begin Source File + +SOURCE=.\XmlOutputterHook.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\include\cppunit\XmlOutputterHook.h +# End Source File # End Group # Begin Group "core" @@ -485,11 +493,11 @@ SOURCE=..\..\include\cppunit\plugin\TestPlugIn.h # End Source File # Begin Source File -SOURCE=.\TestPlugInAdapter.cpp +SOURCE=.\TestPlugInDefaultImpl.cpp # End Source File # Begin Source File -SOURCE=..\..\include\cppunit\plugin\TestPlugInAdapter.h +SOURCE=..\..\include\cppunit\plugin\TestPlugInDefaultImpl.h # End Source File # Begin Source File @@ -505,6 +513,14 @@ SOURCE=.\Win32DynamicLibraryManager.cpp # PROP Default_Filter "" # Begin Source File +SOURCE=.\StringTools.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\include\cppunit\tools\StringTools.h +# End Source File +# Begin Source File + SOURCE=.\XmlDocument.cpp # End Source File # Begin Source File diff --git a/src/cppunit/cppunit_dll.dsp b/src/cppunit/cppunit_dll.dsp index 4336150..4ab8bab 100644 --- a/src/cppunit/cppunit_dll.dsp +++ b/src/cppunit/cppunit_dll.dsp @@ -349,6 +349,14 @@ SOURCE=.\XmlOutputter.cpp SOURCE=..\..\include\cppunit\XmlOutputter.h # End Source File +# Begin Source File + +SOURCE=.\XmlOutputterHook.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\include\cppunit\XmlOutputterHook.h +# End Source File # End Group # Begin Group "portability" @@ -487,11 +495,11 @@ SOURCE=..\..\include\cppunit\plugin\TestPlugIn.h # End Source File # Begin Source File -SOURCE=.\TestPlugInAdapter.cpp +SOURCE=.\TestPlugInDefaultImpl.cpp # End Source File # Begin Source File -SOURCE=..\..\include\cppunit\plugin\TestPlugInAdapter.h +SOURCE=..\..\include\cppunit\plugin\TestPlugInDefaultImpl.h # End Source File # Begin Source File @@ -507,6 +515,14 @@ SOURCE=.\Win32DynamicLibraryManager.cpp # PROP Default_Filter "" # Begin Source File +SOURCE=.\StringTools.cpp +# End Source File +# Begin Source File + +SOURCE=..\..\include\cppunit\tools\StringTools.h +# End Source File +# Begin Source File + SOURCE=.\XmlDocument.cpp # End Source File # Begin Source File diff --git a/src/msvc6/testpluginrunner/TestPlugInRunner.dsp b/src/msvc6/testpluginrunner/TestPlugInRunner.dsp index 2b34d35..294cfa5 100644 --- a/src/msvc6/testpluginrunner/TestPlugInRunner.dsp +++ b/src/msvc6/testpluginrunner/TestPlugInRunner.dsp @@ -256,32 +256,12 @@ InputName=cppunitd_dll # Begin Source File SOURCE=..\..\..\lib\testrunner.dll - -!IF "$(CFG)" == "TestPlugInRunner - Win32 Release" - -# PROP Exclude_From_Build 1 - -!ELSEIF "$(CFG)" == "TestPlugInRunner - Win32 Debug" - # PROP Exclude_From_Build 1 - -!ENDIF - # End Source File # Begin Source File SOURCE=..\..\..\lib\testrunnerd.dll - -!IF "$(CFG)" == "TestPlugInRunner - Win32 Release" - -# PROP Exclude_From_Build 1 - -!ELSEIF "$(CFG)" == "TestPlugInRunner - Win32 Debug" - # PROP Exclude_From_Build 1 - -!ENDIF - # End Source File # End Group # Begin Group "TestRunner-Was-In-Dll" |