views:

201

answers:

2

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!

+1  A: 

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.

Stuart Lange
A: 

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>