changeset 269:51580a463e3e

Add `<c:cppunit>` recipe command, based on patch by Chandler Carruth and examples by Akos Maroy.
author cmlenz
date Fri, 07 Oct 2005 10:44:20 +0000
parents 4a6545360c23
children 76bfc58fc394
files ChangeLog bitten/build/ctools.py bitten/build/javatools.py bitten/build/tests/__init__.py bitten/build/tests/ctools.py
diffstat 5 files changed, 163 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog
+++ b/ChangeLog
@@ -9,14 +9,14 @@
  * Improvements to the build status presentation in Trac.
  * Changes to the build recipe format. See the documentation on the web
    site for details.
- * New recipe commands: <sh:pipe>, <c:configure>, <java:ant>,
-   <java:junit>, and <x:transform>. Various improvements to the
-   existing commands.
+ * New recipe commands: <sh:pipe>, <c:configure>, <c:cppunit>,
+   <java:ant>, <java:junit>, and <x:transform>. Various improvements to
+   the existing commands.
  * Recipe commands and command attributes in recipes can now reference
    slave configuration values.
  * The names of the master and slaves scripts have changed: `bittend`
    is now `bitten-master`, `bitten` is now `bitten-slave`.
- * The build master can now handle multiple Trac environments (projects).
+ * The build master can now handle multiple Trac environments.
  * The build slave now by default removes any working directories when
    done.
  * Build configurations can now be completely deleted.
--- a/bitten/build/ctools.py
+++ b/bitten/build/ctools.py
@@ -55,3 +55,64 @@
     returncode = shtools.execute(ctxt, executable='make', args=args)
     if returncode != 0:
         ctxt.error('make failed (%s)' % cmdline.returncode)
+
+def cppunit(ctxt, file_=None, srcdir=None):
+    """Collect CppUnit XML data."""
+    assert file_, 'Missing required attribute "file"'
+
+    try:
+        fileobj = file(ctxt.resolve(file_), 'r')
+        try:
+            total, failed = 0, 0
+            results = xmlio.Fragment()
+            for group in xmlio.parse(fileobj):
+                if group.name not in ('FailedTests', 'SuccessfulTests'):
+                    continue
+                for child in group.children():
+                    test = xmlio.Element('test')
+                    name = child.children('Name').next().gettext()
+                    if '::' in name:
+                        parts = name.split('::')
+                        test.attr['fixture'] = '::'.join(parts[:-1])
+                        name = parts[-1]
+                    test.attr['name'] = name
+
+                    for location in child.children('Location'):
+                        for file_elem in location.children('File'):
+                            filepath = file_elem.gettext()
+                            if srcdir is not None:
+                                filepath = posixpath.join(srcdir, filepath)
+                            test.attr['file'] = filepath
+                            break
+                        for line_elem in location.children('Line'):
+                            test.attr['line'] = line_elem.gettext()
+                            break
+                        break
+
+                    if child.name == 'FailedTest':
+                        for message in child.children('Message'):
+                            test.append(xmlio.Element('traceback')[
+                                message.gettext()
+                            ])
+                        test.attr['status'] = 'failure'
+                        failed += 1
+                    else:
+                        test.attr['status'] = 'success'
+
+                    results.append(test)
+                    total += 1
+
+            if failed:
+                ctxt.error('%d of %d test%s failed' % (failed, total,
+                           total != 1 and 's' or ''))
+
+            ctxt.report('test', results)
+
+        finally:
+            fileobj.close()
+
+    except IOError, e:
+        log.warning('Error opening CppUnit results file (%s)', e)
+    except xmlio.ParseError, e:
+        print e
+        log.warning('Error parsing CppUnit results file (%s)', e)
--- a/bitten/build/javatools.py
+++ b/bitten/build/javatools.py
@@ -7,12 +7,12 @@
 # you should have received as part of this distribution. The terms
 # are also available at http://bitten.cmlenz.net/wiki/License.
 
+from glob import glob
 import logging
 import os
-import tempfile
+import posixpath
 import shlex
-import posixpath
-from glob import glob
+import tempfile
 
 from bitten.build import CommandLine
 from bitten.util import xmlio
@@ -91,7 +91,7 @@
                     test.attr['fixture'] = testcase.attr['classname']
                     if 'time' in testcase.attr:
                         test.attr['duration'] = testcase.attr['time']
-                    if srcdir:
+                    if srcdir is not None:
                         cls = testcase.attr['classname'].split('.')
                         test.attr['file'] = posixpath.join(srcdir, *cls) + \
                                             '.java'
--- a/bitten/build/tests/__init__.py
+++ b/bitten/build/tests/__init__.py
@@ -9,12 +9,13 @@
 
 import unittest
 
-from bitten.build.tests import api, config, pythontools, xmltools
+from bitten.build.tests import api, config, ctools, pythontools, xmltools
 
 def suite():
     suite = unittest.TestSuite()
     suite.addTest(api.suite())
     suite.addTest(config.suite())
+    suite.addTest(ctools.suite())
     suite.addTest(pythontools.suite())
     suite.addTest(xmltools.suite())
     return suite
new file mode 100644
--- /dev/null
+++ b/bitten/build/tests/ctools.py
@@ -0,0 +1,92 @@
+# -*- coding: iso8859-1 -*-
+#
+# Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de>
+# All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://bitten.cmlenz.net/wiki/License.
+
+import os
+import shutil
+import tempfile
+import unittest
+
+from bitten.build import ctools
+from bitten.recipe import Context, Recipe
+
+
+class CppUnitTestCase(unittest.TestCase):
+
+    def setUp(self):
+        self.basedir = os.path.realpath(tempfile.mkdtemp())
+        self.ctxt = Context(self.basedir)
+
+    def tearDown(self):
+        shutil.rmtree(self.basedir)
+
+    def test_missing_param_file(self):
+        self.assertRaises(AssertionError, ctools.cppunit, self.ctxt)
+
+    def test_empty_summary(self):
+        cppunit_xml = file(self.ctxt.resolve('cppunit.xml'), 'w')
+        cppunit_xml.write("""<?xml version="1.0" encoding='utf-8' ?>
+<TestRun>
+  <FailedTests>
+    <FailedTest id="2">
+      <Name>HelloTest::secondTest</Name>
+      <FailureType>Assertion</FailureType>
+      <Location>
+        <File>HelloTest.cxx</File>
+        <Line>95</Line>
+      </Location>
+      <Message>assertion failed
+- Expression: 2 == 3
+</Message>
+    </FailedTest>
+  </FailedTests>
+  <SuccessfulTests>
+    <Test id="1">
+      <Name>HelloTest::firstTest</Name>
+    </Test>
+    <Test id="3">
+      <Name>HelloTest::thirdTest</Name>
+    </Test>
+  </SuccessfulTests>
+  <Statistics>
+    <Tests>3</Tests>
+    <FailuresTotal>1</FailuresTotal>
+    <Errors>0</Errors>
+    <Failures>1</Failures>
+  </Statistics>
+</TestRun>""")
+        cppunit_xml.close()
+        ctools.cppunit(self.ctxt, file_='cppunit.xml')
+        type, category, generator, xml = self.ctxt.output.pop()
+        self.assertEqual(Recipe.REPORT, type)
+        self.assertEqual('test', category)
+
+        tests = list(xml.children)
+        self.assertEqual(3, len(tests))
+        self.assertEqual('HelloTest', tests[0].attr['fixture'])
+        self.assertEqual('secondTest', tests[0].attr['name'])
+        self.assertEqual('failure', tests[0].attr['status'])
+        self.assertEqual('HelloTest.cxx', tests[0].attr['file'])
+        self.assertEqual('95', tests[0].attr['line'])
+
+        self.assertEqual('HelloTest', tests[1].attr['fixture'])
+        self.assertEqual('firstTest', tests[1].attr['name'])
+        self.assertEqual('success', tests[1].attr['status'])
+
+        self.assertEqual('HelloTest', tests[2].attr['fixture'])
+        self.assertEqual('thirdTest', tests[2].attr['name'])
+        self.assertEqual('success', tests[2].attr['status'])
+
+
+def suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(CppUnitTestCase, 'test'))
+    return suite
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='suite')
Copyright (C) 2012-2017 Edgewall Software