summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBaptiste Lepilleur <gaiacrtn@free.fr>2002-06-14 19:21:01 +0000
committerBaptiste Lepilleur <gaiacrtn@free.fr>2002-06-14 19:21:01 +0000
commit73a038f1eaa268cec330d971fb550befec6f7798 (patch)
treec3eba5d793e37413889acad5b0b9f70caf89b0f6
parentf39e160fba25476de7d41e2f19d756db7ee76dc7 (diff)
Include/cppunit/plugin/PlugInManager.
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.
-rw-r--r--ChangeLog59
-rw-r--r--NEWS70
-rw-r--r--TODO6
-rw-r--r--examples/ClockerPlugIn/ClockerListener.cpp130
-rw-r--r--examples/ClockerPlugIn/ClockerListener.h52
-rw-r--r--examples/ClockerPlugIn/ClockerModel.cpp145
-rw-r--r--examples/ClockerPlugIn/ClockerModel.h95
-rw-r--r--examples/ClockerPlugIn/ClockerPlugIn.cpp30
-rw-r--r--examples/ClockerPlugIn/ClockerPlugIn.dsp22
-rw-r--r--examples/ClockerPlugIn/ClockerXmlHook.cpp94
-rw-r--r--examples/ClockerPlugIn/ClockerXmlHook.h62
-rw-r--r--examples/ClockerPlugIn/ReadMe.txt42
-rw-r--r--examples/DumperPlugIn/DumperPlugIn.cpp10
-rw-r--r--examples/ReadMe.txt3
-rw-r--r--examples/cppunittest/XmlElementTest.cpp116
-rw-r--r--examples/cppunittest/XmlElementTest.h24
-rw-r--r--examples/cppunittest/XmlOutputterTest.cpp85
-rw-r--r--examples/cppunittest/XmlOutputterTest.h5
-rw-r--r--examples/examples.optbin194048 -> 195072 bytes
-rw-r--r--include/cppunit/Makefile.am3
-rw-r--r--include/cppunit/XmlOutputter.h53
-rw-r--r--include/cppunit/XmlOutputterHook.h46
-rw-r--r--include/cppunit/plugin/Makefile.am2
-rw-r--r--include/cppunit/plugin/PlugInManager.h11
-rw-r--r--include/cppunit/plugin/TestPlugIn.h17
-rw-r--r--include/cppunit/plugin/TestPlugInDefaultImpl.h (renamed from include/cppunit/plugin/TestPlugInAdapter.h)10
-rw-r--r--include/cppunit/tools/Makefile.am1
-rw-r--r--include/cppunit/tools/StringTools.h27
-rw-r--r--include/cppunit/tools/XmlElement.h85
-rw-r--r--src/DllPlugInTester/DllPlugInTester.cpp136
-rw-r--r--src/cppunit/Makefile.am6
-rw-r--r--src/cppunit/PlugInManager.cpp17
-rw-r--r--src/cppunit/StringTools.cpp29
-rw-r--r--src/cppunit/TestPlugInAdapter.cpp51
-rw-r--r--src/cppunit/TestPlugInDefaultImpl.cpp63
-rw-r--r--src/cppunit/XmlElement.cpp86
-rw-r--r--src/cppunit/XmlOutputter.cpp74
-rw-r--r--src/cppunit/XmlOutputterHook.cpp47
-rw-r--r--src/cppunit/cppunit.dsp20
-rw-r--r--src/cppunit/cppunit_dll.dsp20
-rw-r--r--src/msvc6/testpluginrunner/TestPlugInRunner.dsp20
41 files changed, 1518 insertions, 356 deletions
diff --git a/ChangeLog b/ChangeLog
index deca6ea..85b71e1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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.
diff --git a/NEWS b/NEWS
index aa53d27..6b99729 100644
--- a/NEWS
+++ b/NEWS
@@ -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
diff --git a/TODO b/TODO
index c7f0b99..569f457 100644
--- a/TODO
+++ b/TODO
@@ -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 &parameters )
{
- 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 &copy );
diff --git a/examples/examples.opt b/examples/examples.opt
index 1cc6969..9fe4d1c 100644
--- a/examples/examples.opt
+++ b/examples/examples.opt
Binary files differ
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 &parameters );
@@ -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 &parameters )
-{
-}
-
-
-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 &parameters )
+{
+}
+
+
+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"