Mercurial > bitten > bitten-test
changeset 125:92e29e6b4c16
* The {{{python:trace>}}} recipe command now transmits coverage statistics to the build master. Closes #33.
* Added unit tests for {{{<python:trace>}}} and {{{<python:unittest>}}}.
author | cmlenz |
---|---|
date | Tue, 09 Aug 2005 18:02:21 +0000 |
parents | 540ffa358d80 |
children | c2877512beb8 |
files | bitten/build/pythontools.py bitten/build/tests/__init__.py bitten/build/tests/pythontools.py bitten/tests/__init__.py bitten/tests/recipe.py bitten/tests/store.py |
diffstat | 6 files changed, 231 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/bitten/build/pythontools.py +++ b/bitten/build/pythontools.py @@ -19,6 +19,7 @@ # Author: Christopher Lenz <cmlenz@gmx.de> import logging +import os import re from bitten.build import BuildError @@ -84,6 +85,49 @@ assert summary, 'Missing required attribute "summary"' assert coverdir, 'Missing required attribute "coverdir"' + summary_line_re = re.compile(r'^\s*(?P<lines>\d+)\s+(?P<cov>\d+)%\s+' + r'(?P<module>.*?)\s+\((?P<filename>.*?)\)') + coverage_line_re = re.compile(r'\s*(?:(?P<hits>\d+): )?(?P<line>.*)') + + try: + summary_file = open(ctxt.resolve(summary), 'r') + try: + coverage = xmlio.Fragment() + for summary_line in summary_file: + match = summary_line_re.search(summary_line) + if match: + filename = match.group(4) + modname = match.group(3) + cov = int(match.group(2)) + if filename.startswith(ctxt.basedir): + module = xmlio.Element('coverage', file=filename, + module=modname, percentage=cov) + coverage_path = ctxt.resolve(coverdir, + modname + '.cover') + if not os.path.exists(coverage_path): + log.warning('No coverage file for module %s at %s', + modname, coverage_path) + continue + coverage_file = open(coverage_path, 'r') + try: + for num, coverage_line in enumerate(coverage_file): + match = coverage_line_re.search(coverage_line) + if match: + hits = match.group(1) + if hits: + line = xmlio.Element('line', line=num, + hits=int(hits)) + module.append(line) + finally: + coverage_file.close() + coverage.append(module) + ctxt.report(coverage) + finally: + summary_file.close() + except IOError, e: + raise BuildError, 'Error opening unittest results file (%s)' % e + + def unittest(ctxt, file=None): """Extract data from a unittest results file in XML format.""" assert file, 'Missing required attribute "file"' @@ -93,6 +137,9 @@ try: results = xmlio.Fragment() for child in xmlio.parse(fd).children(): + filename = child.attr.get('file') + if filename and filename.startswith(ctxt.basedir): + child.attr['file'] = filename[len(ctxt.basedir) + 1:] results.append(child) ctxt.report(results) finally:
new file mode 100644 --- /dev/null +++ b/bitten/build/tests/__init__.py @@ -0,0 +1,32 @@ +# -*- coding: iso8859-1 -*- +# +# Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de> +# +# Bitten is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# Trac is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# Author: Christopher Lenz <cmlenz@gmx.de> + +import doctest +import unittest + +from bitten.build.tests import pythontools + +def suite(): + suite = unittest.TestSuite() + suite.addTest(pythontools.suite()) + return suite + +if __name__ == '__main__': + unittest.main(defaultTest='suite')
new file mode 100644 --- /dev/null +++ b/bitten/build/tests/pythontools.py @@ -0,0 +1,143 @@ +# -*- coding: iso8859-1 -*- +# +# Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de> +# +# Bitten is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of the +# License, or (at your option) any later version. +# +# Trac is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# +# Author: Christopher Lenz <cmlenz@gmx.de> + +import os +import os.path +import shutil +import tempfile +import unittest + +from bitten.build import pythontools, BuildError +from bitten.recipe import Context, Recipe + + +class TraceTestCase(unittest.TestCase): + + def setUp(self): + self.temp_dir = tempfile.gettempdir() + self.ctxt = Context(self.temp_dir) + self.summary = open(os.path.join(self.temp_dir, 'test-coverage.txt'), + 'w') + self.coverdir = os.path.join(self.temp_dir, 'coverage') + os.mkdir(self.coverdir) + + def tearDown(self): + shutil.rmtree(self.coverdir) + os.unlink(self.summary.name) + + def test_missing_param_summary(self): + self.assertRaises(AssertionError, pythontools.trace, self.ctxt, + coverdir='coverage') + + def test_missing_param_coverdir(self): + self.assertRaises(AssertionError, pythontools.trace, self.ctxt, + summary='test-coverage.txt') + + def test_empty_summary(self): + self.summary.write('line cov% module (path)') + self.summary.close() + pythontools.trace(self.ctxt, summary=self.summary.name, + coverdir=self.coverdir) + type, function, xml = self.ctxt.output.pop() + self.assertEqual(Recipe.REPORT, type) + self.assertEqual(0, len(xml.children)) + + + +class UnittestTestCase(unittest.TestCase): + + def setUp(self): + self.temp_dir = tempfile.gettempdir() + self.ctxt = Context(self.temp_dir) + self.results_xml = open(os.path.join(self.temp_dir, 'test-results.xml'), + 'w') + + def tearDown(self): + os.unlink(os.path.join(self.temp_dir, 'test-results.xml')) + + def test_missing_file_param(self): + self.assertRaises(AssertionError, pythontools.unittest, self.ctxt) + + def test_invalid_file_param(self): + self.assertRaises(BuildError, + pythontools.unittest, self.ctxt, file='foobar') + + def test_empty_results(self): + self.results_xml.write('<?xml version="1.0"?>' + '<unittest-results>' + '</unittest-results>') + self.results_xml.close() + pythontools.unittest(self.ctxt, self.results_xml.name) + type, function, xml = self.ctxt.output.pop() + self.assertEqual(Recipe.REPORT, type) + self.assertEqual(0, len(xml.children)) + + def test_successful_test(self): + self.results_xml.write('<?xml version="1.0"?>' + '<unittest-results>' + '<test duration="0.12" status="success"' + ' file="bar_test.py"' + ' name="test_foo (pkg.BarTestCase)"/>' + '</unittest-results>') + self.results_xml.close() + pythontools.unittest(self.ctxt, self.results_xml.name) + type, function, xml = self.ctxt.output.pop() + self.assertEqual(Recipe.REPORT, type) + self.assertEqual(1, len(xml.children)) + test_elem = xml.children[0] + self.assertEqual('test', test_elem.name) + self.assertEqual('0.12', test_elem.attr['duration']) + self.assertEqual('success', test_elem.attr['status']) + self.assertEqual('bar_test.py', test_elem.attr['file']) + self.assertEqual('test_foo (pkg.BarTestCase)', test_elem.attr['name']) + + def test_file_path_normalization(self): + self.results_xml.write('<?xml version="1.0"?>' + '<unittest-results>' + '<test duration="0.12" status="success"' + ' file="%s"' + ' name="test_foo (pkg.BarTestCase)"/>' + '</unittest-results>' + % os.path.join(self.ctxt.basedir, 'bar_test.py')) + self.results_xml.close() + pythontools.unittest(self.ctxt, self.results_xml.name) + type, function, xml = self.ctxt.output.pop() + self.assertEqual('bar_test.py', xml.children[0].attr['file']) + + def test_missing_file_attribute(self): + self.results_xml.write('<?xml version="1.0"?>' + '<unittest-results>' + '<test duration="0.12" status="success"' + ' name="test_foo (pkg.BarTestCase)"/>' + '</unittest-results>') + self.results_xml.close() + pythontools.unittest(self.ctxt, self.results_xml.name) + type, function, xml = self.ctxt.output.pop() + self.assertEqual(None, xml.children[0].attr.get('file')) + + +def suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TraceTestCase, 'test')) + suite.addTest(unittest.makeSuite(UnittestTestCase, 'test')) + return suite + +if __name__ == '__main__': + unittest.main(defaultTest='suite')
--- a/bitten/tests/__init__.py +++ b/bitten/tests/__init__.py @@ -21,6 +21,7 @@ import unittest from bitten.tests import model, recipe, store +from bitten.build import tests as build from bitten.util import tests as util from bitten.trac_ext import tests as trac_ext @@ -29,6 +30,7 @@ suite.addTest(model.suite()) suite.addTest(recipe.suite()) suite.addTest(store.suite()) + suite.addTest(build.suite()) suite.addTest(trac_ext.suite()) suite.addTest(util.suite()) return suite