# HG changeset patch # User cmlenz # Date 1128681860 0 # Node ID 51580a463e3e7b6f1c9976aa8d695d4050d0c48c # Parent 4a6545360c2370b99e2a05e3e5c3ea622cb9d1a6 Add `` recipe command, based on patch by Chandler Carruth and examples by Akos Maroy. diff --git a/ChangeLog b/ChangeLog --- 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: , , , - , and . Various improvements to the - existing commands. + * New recipe commands: , , , + , , and . 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. diff --git a/bitten/build/ctools.py b/bitten/build/ctools.py --- 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) diff --git a/bitten/build/javatools.py b/bitten/build/javatools.py --- 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' diff --git a/bitten/build/tests/__init__.py b/bitten/build/tests/__init__.py --- 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 diff --git a/bitten/build/tests/ctools.py b/bitten/build/tests/ctools.py new file mode 100644 --- /dev/null +++ b/bitten/build/tests/ctools.py @@ -0,0 +1,92 @@ +# -*- coding: iso8859-1 -*- +# +# Copyright (C) 2005 Christopher Lenz +# 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(""" + + + + HelloTest::secondTest + Assertion + + HelloTest.cxx + 95 + + assertion failed +- Expression: 2 == 3 + + + + + + HelloTest::firstTest + + + HelloTest::thirdTest + + + + 3 + 1 + 0 + 1 + +""") + 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')