I have some C++ projects running through cruisecontrol.net. As a part of the build process, we compile and run Boost.Test unit test suites. I have these configured to dump XML log files. While the format is similar to JUnit/NUnit, it's not quite the same (and lacks some information), so cruisecontrol.net is unable to pick them up. I am wondering if anyone has created (or knows of) an existing XSL transform that will convert Boost.Test results to JUnit/NUnit format, or alternatively, directly to a presentable (html) format.
Thanks!
The standard way to transform XML data into other formats is by Extensible Stylesheet Language Transformations (XSLT). You can use the built-in XSLTRANSFORM function to convert XML documents into HTML, plain text, or different XML schemas.
XSLT <xsl:template> The match attribute is used to associate the template with an XML element. The match attribute can also be used to define a template for a whole branch of the XML document (i.e. match="/" defines the whole document).
I'm working on rolling my own Boost.Test -> JUnit XSL. Please note that this is intended to consume the XML report output from Boost.Test - not the log output. This is a work in progress - here's what I have so far:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl">
<xsl:output method="xml"
indent="yes"/>
<xsl:template match="TestResult">
<test-results>
<xsl:attribute name="total">
<xsl:value-of select="sum(./TestSuite/@test_cases_passed) + sum(./TestSuite/@test_cases_failed) + sum(./TestSuite/@test_cases_skipped) + sum(./TestSuite/@test_cases_aborted)"/>
</xsl:attribute>
<xsl:attribute name="failures">
<xsl:value-of select="sum(./TestSuite/@test_cases_failed) + sum(./TestSuite/@test_cases_aborted)"/>
</xsl:attribute>
<xsl:attribute name="skipped">
<xsl:value-of select="sum(./TestSuite/@test_cases_skipped)"/>
</xsl:attribute>
<xsl:attribute name="not-run">
<xsl:value-of select="sum(./TestSuite/@test_cases_skipped)"/>
</xsl:attribute>
<xsl:call-template name="testSuite" />
</test-results>
</xsl:template>
<xsl:template name="testSuite">
<xsl:for-each select="TestSuite">
<test-suite>
<xsl:call-template name="testAttributes" />
<results>
<xsl:call-template name="testSuite" />
<xsl:for-each select="TestCase">
<test-case>
<xsl:call-template name="testAttributes" />
</test-case>
</xsl:for-each>
</results>
</test-suite>
</xsl:for-each>
</xsl:template>
<xsl:template name="testAttributes">
<xsl:attribute name="name">
<xsl:value-of select="@name"/>
</xsl:attribute>
<xsl:attribute name="success">
<xsl:choose>
<xsl:when test="@result = 'passed'">True</xsl:when>
<xsl:when test="@result != 'passed'">False</xsl:when>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="executed">True</xsl:attribute>
<xsl:attribute name="time">0</xsl:attribute>
<xsl:attribute name="asserts">
<xsl:value-of select="@assertions_failed + @assertions_passed"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
I have this integrated into my build process and it's getting picked up and processed by ccnet nicely. It's not perfect, but it works better than the complete lack of reporting I had before. I'm open to suggestions on how to map the Boost.Test data to the "total", "failures", "skipped", and "not-run" fields of the JUnit report. Also, unfortunately the error detail data (indicating the nature of the failure and the file/line number where the failure occurred) are only printed to the log, not to the report, so I would have to "merge" the two to get all the data I would ideally like to have.
Thanks to Stuart Lange's answer above, I was able to get Boost.Test's output to integrate with our Bamboo build system. Bamboo describes the format they can ingest here:
http://confluence.atlassian.com/display/BAMBOO/JUnit+parsing+in+Bamboo
So I used Stuart Lange's XSL above as a starting point, and ended up with this:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl">
<xsl:output method="xml"
indent="yes"/>
<xsl:template match="TestResult">
<xsl:call-template name="testSuite" />
</xsl:template>
<xsl:template name="testSuite">
<xsl:for-each select="TestSuite">
<testsuite>
<xsl:attribute name="errors">
<xsl:value-of select="@test_cases_failed + @test_cases_aborted"/>
</xsl:attribute>
<xsl:attribute name="tests">
<xsl:value-of select="@test_cases_passed + @test_cases_failed + @test_cases_skipped + @test_cases_aborted"/>
</xsl:attribute>
<xsl:attribute name="skipped">
<xsl:value-of select="@test_cases_skipped"/>
</xsl:attribute>
<xsl:attribute name="failures">
<xsl:value-of select="@test_cases_failed"/>
</xsl:attribute>
<xsl:call-template name="testAttributes" />
<!--results-->
<xsl:call-template name="testSuite" />
<xsl:for-each select="TestCase">
<testcase>
<xsl:call-template name="testAttributes" />
<xsl:call-template name="testCaseElements" />
</testcase>
</xsl:for-each>
<!--/results-->
</testsuite>
</xsl:for-each>
</xsl:template>
<xsl:template name="testAttributes">
<xsl:attribute name="name">
<xsl:value-of select="@name"/>
</xsl:attribute>
<xsl:attribute name="success">
<xsl:choose>
<xsl:when test="@result = 'passed'">True</xsl:when>
<xsl:when test="@result != 'passed'">False</xsl:when>
</xsl:choose>
</xsl:attribute>
<xsl:attribute name="executed">True</xsl:attribute>
<xsl:attribute name="time">0</xsl:attribute>
<xsl:attribute name="asserts">
<xsl:value-of select="@assertions_failed + @assertions_passed"/>
</xsl:attribute>
</xsl:template>
<xsl:template name="testCaseElements">
<xsl:if test="@result != 'passed'">
<failure type="No type reported" message="No message reported"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
We use the following command line options when we call our Boost.Test executable:
--report_format=xml
--report_level=detailed
It's not the hottest XSL to leave the presses, but it's enough to see the suites, the tests contained, and which ones (if any) failed from Bamboo.
One additional note, with this method you only want to capture the stderr output. The contents of stdout (at least the way we use Boost.Test) will break things.
This xsl works for me for converting Boost.Test's log.xml to JUnit xml readable by Bamboo, complete with messages!: https://issues.jenkins-ci.org/secure/attachment/19613/boosttest-1.0-to-junit-1.0.xsl
It's linked from here: https://issues.jenkins-ci.org/browse/JENKINS-7039
We run our tests with --report_format=xml --report_level=detailed --log_level=test_suite --log_format=xml
. You need both stderr and stdout, then we replace <TestLog> with <xml><TestLog> and replace </TestResult> with </TestResult><xml>. After that we run it though tidy and finally run that though this xslt.
You must also be careful not to have any xml-style tags in your stdout/err. Things like <foo> can break the conversion.
<xsl:for-each select="./TestSuite">
<xsl:variable name="name2" select="@name"/>
<testsuite>
<xsl:attribute name="errors">
<xsl:value-of select="@test_cases_failed" />
</xsl:attribute>
<xsl:attribute name="tests">
<xsl:value-of select="@test_cases_failed + @test_cases_passed + @test_cases_skipped" />
</xsl:attribute>
<xsl:attribute name="name">
<xsl:value-of select="@name" />
</xsl:attribute>
<xsl:for-each select="./TestCase">
<xsl:variable name="name3" select="@name"/>
<testcase>
<xsl:attribute name="name">
<xsl:value-of select="@name" />
</xsl:attribute>
<xsl:for-each select="/xml/TestLog/TestSuite[@name=$name1]">
<xsl:for-each select="./TestSuite[@name=$name2]">
<xsl:for-each select="./TestCase[@name=$name3]">
<xsl:for-each select="./TestingTime">
<xsl:attribute name="time">
<xsl:value-of select="./text() div 100000"/>
</xsl:attribute>
</xsl:for-each>
<xsl:for-each select="./Error">
<failure>
<xsl:attribute name="type">AssertionFailedError</xsl:attribute>
<xsl:attribute name="message">
<xsl:value-of select="@file"/>:<xsl:value-of select="@line"/>
</xsl:attribute>
<xsl:copy-of select="./text()"/>
</failure>
</xsl:for-each>
<xsl:for-each select="./Exception">
<failure>
<xsl:attribute name="type">AssertionFailedException</xsl:attribute>
<xsl:attribute name="message">
<xsl:value-of select="@file"/>:<xsl:value-of select="@line"/>
</xsl:attribute>
<xsl:copy-of select="./text()"/>
</failure>
</xsl:for-each>
<system-out>
<xsl:copy-of select="./text()"/>
</system-out>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</testcase>
</xsl:for-each>
</testsuite>
</xsl:for-each>
</testsuite>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With