Using Hudson for C++/CMake/CppUnit
时间:2010-10-11 来源:kevinzhwl
As a follow-up to Using grails projects in Hudson, here is another not-so-standard usage of Hudson: C++ projects with CMake and CppUnit. Let’s see how that works out.
As long as you have Java/Ant/JUnit based projects, a fine tool that it is, configuration of Hudson is pretty straight forward. But if you have a C++ project with CMake as build system and CppUnit for your unit testing, you have to dig a little deeper. Fortunately, Hudson provides the possibility to execute arbitrary shell commands. So in order to build the project and execute the tests, we can simply put a shell script to work:
# define build and installation directories BUILD_DIR=$WORKSPACE/build_dir INSTALL_DIR=$WORKSPACE/install_dir # we want to have a clean build rm -Rf $BUILD_DIR mkdir $BUILD_DIR cd $BUILD_DIR # initializing the build system cmake .. -DCMAKE_INSTALL_PREFIX=$INSTALL_DIR # fire-up the compiler make install
Environment variable WORKSPACE is defined by Hudson. Other useful variables are e.g. BUILD_NUMBER, BUILD_TAG and CVS_BRANCH.
But what about those unit tests? Hudson understands JUnit test result files out-of-the-box. So all we have to do is make CppUnit spit out an xml report and then translate it to JUnit form. To help us with that, we need a little xslt transformation. But first, let’s see how we can make CppUnit generate xml results (a little simplified):
#include <cppunit/necessary/CppUnitIncludes/> ... using namespace std; using namespace CppUnit; int main(int argc, char** argv) { TestResult controller; TestResultCollector result; controller.addListener(&result); CppUnit::TextUi::TestRunner runner; runner.addTest( TestFactoryRegistry::getRegistry().makeTest() ); runner.run(controller); // important stuff happens next ofstream xmlFileOut("cpptestresults.xml"); XmlOutputter xmlOut(&result, xmlFileOut); xmlOut.write(); }
The assumption here is that your unit tests are built into libraries that are linked with the main function above. To execute the unit tests we add the following to out shell script:
export PATH=$INSTALL_DIR/bin:$PATH export LD_LIBRARY_PATH=$INSTALL_DIR/lib:$LD_LIBRARY_PATH # call the cppunit executable cd $WORKSPACE cppunittests
This results in CppUnit generating file $WORKSPACE/cpptestresults.xml. Now, with the help of a little program called xsltproc and the following little piece of XSLT code, we can translate cpptestresults.xml to testresults.xml in JUnit format.
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" indent="yes"/> <xsl:template match="/"> <testsuite> <xsl:attribute name="errors"><xsl:value-of select="TestRun/Statistics/Errors"/></xsl:attribute> <xsl:attribute name="failures"> <xsl:value-of select="TestRun/Statistics/Failures"/> </xsl:attribute> <xsl:attribute name="tests"> <xsl:value-of select="TestRun/Statistics/Tests"/> </xsl:attribute> <xsl:attribute name="name">from cppunit</xsl:attribute> <xsl:apply-templates/> </testsuite> </xsl:template> <xsl:template match="/TestRun/SuccessfulTests/Test"> <testcase> <xsl:attribute name="classname" ><xsl:value-of select="substring-before(Name, '::')"/></xsl:attribute> <xsl:attribute name="name"><xsl:value-of select="substring-after(Name, '::')"/></xsl:attribute> </testcase> </xsl:template> <xsl:template match="/TestRun/FailedTests/FailedTest"> <testcase> <xsl:attribute name="classname" ><xsl:value-of select="substring-before(Name, '::')"/></xsl:attribute> <xsl:attribute name="name"><xsl:value-of select="substring-after(Name, '::')"/></xsl:attribute> <error> <xsl:attribute name="message"> <xsl:value-of select=" normalize-space(Message)"/> </xsl:attribute> <xsl:attribute name="type"> <xsl:value-of select="FailureType"/> </xsl:attribute> <xsl:value-of select="Message"/> File:<xsl:value-of select="Location/File"/> Line:<xsl:value-of select="Location/Line"/> </error> </testcase> </xsl:template> <xsl:template match="text()|@*"/> </xsl:stylesheet>
The following call goes into our shell script:
xsltproc cppunit2junit.xsl $WORKSPACE/cpptestresults.xml > $WORKSPACE/testresults.xml
In the configuration page we can now check “Display JUnit test results” and give testresults.xml as result file. As a last step, we can package everything in $WORKSPACE/install_dir into a .tgz file and have Hudson to store it as build artifact. That’s it!
As always, there is room for improvements. One would be to wrap the shell script code above in a separate bash script and have Hudson simply call that script. The only advantage of the approach above is that you can see what’s going on directly on the configuration page. If your project is bigger, you might have more than one CppUnit executable. In this case, you can for example generate all testresult.xml files into a separate directory and tell Hudson to take into account all .xml files there.
Update: For the CMake related part of the above shell script I recently published the first version of a cmakebuilder plugin for Hudson. Check out my corresponding blog post.
Possibly related posts: (automatically generated)
- Open Source Love Day March 2010
- Music News: Jennifer Hudson Sang Her Heart Out!
- Sonar – Hudson Integration
- Hudson – Tool to build/test your project
This entry was posted on Monday, September 29th, 2008 at 8:01 am and is filed under C/C++, Continuous Integration. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.