# HG changeset patch # User cmlenz # Date 1116792682 0 # Node ID 3ba3fb6f005435d9f03d36eba36772ba986aa0ab # Parent 32e9f0e94c69e3fc13aa267a88bdea21a8172ebb Generate coverage data in the distutils {{{unittest}}} command. diff --git a/bitten/distutils/testrunner.py b/bitten/distutils/testrunner.py --- a/bitten/distutils/testrunner.py +++ b/bitten/distutils/testrunner.py @@ -1,3 +1,4 @@ +import re import sys import time from distutils.core import Command @@ -6,7 +7,7 @@ from elementtree.ElementTree import Element, ElementTree, SubElement -class FullTestResult(_TextTestResult): +class XMLTestResult(_TextTestResult): def __init__(self, stream, descriptions, verbosity): _TextTestResult.__init__(self, stream, descriptions, verbosity) @@ -14,9 +15,10 @@ def startTest(self, test): _TextTestResult.startTest(self, test) - self.tests.append([test, - sys.modules[test.__module__].__file__, - time.time()]) + filename = sys.modules[test.__module__].__file__ + if filename.endswith('.pyc') or filename.endswith('.pyo'): + filename = filename[:-1] + self.tests.append([test, filename, time.time()]) def stopTest(self, test): _TextTestResult.stopTest(self, test) @@ -30,7 +32,7 @@ self.xml_stream = xml_stream def _makeResult(self): - return FullTestResult(self.stream, self.descriptions, self.verbosity) + return XMLTestResult(self.stream, self.descriptions, self.verbosity) def run(self, test): result = TextTestRunner.run(self, test) @@ -67,27 +69,51 @@ return result -class test(Command): - description = "Runs the unit tests" +class unittest(Command): + description = "Runs the unit tests, and optionally records code coverage" user_options = [('test-suite=', 's', 'Name of the unittest suite to run'), ('xml-output=', None, - 'Path of the XML file where test results are written to')] + 'Path of the XML file where test results are written to'), + ('coverage-dir=', None, + 'Directory where coverage files are to be stored'), + ('coverage-results=', None, + 'Name of the file where the coverage summary should be stored')] def initialize_options(self): self.test_suite = None - self.xml_output = None - self.test_descriptions = None + self.xml_results = None + self.coverage_results = None + self.coverage_dir = None def finalize_options(self): assert self.test_suite, 'Missing required attribute "test-suite"' - if self.xml_output is not None: - self.xml_output = open(self.xml_output, 'w') + if self.xml_results is not None: + self.xml_results = open(self.xml_results, 'w') def run(self): + if self.coverage_dir: + from trace import Trace + trace = Trace(ignoredirs=[sys.prefix, sys.exec_prefix], + trace=False, count=True) + trace.runfunc(self._run_tests) + # make a report, telling it where you want output + results = trace.results() + real_stdout = sys.stdout + sys.stdout = open(self.coverage_results, 'w') + try: + results.write_results(show_missing=True, summary=True, + coverdir=self.coverage_dir) + finally: + sys.stdout.close() + sys.stdout = real_stdout + else: + self._run_tests() + + def _run_tests(self): import unittest suite = __import__(self.test_suite) for comp in self.test_suite.split('.')[1:]: suite = getattr(suite, comp) - runner = XMLTestRunner(stream=sys.stderr, xml_stream=self.xml_output) + runner = XMLTestRunner(stream=sys.stderr, xml_stream=self.xml_results) runner.run(suite.suite()) diff --git a/bitten/recipe.py b/bitten/recipe.py --- a/bitten/recipe.py +++ b/bitten/recipe.py @@ -29,15 +29,24 @@ """ +class Recipe(object): + + def __init__(self, filename='recipe.xml', basedir=os.getcwd()): + self.filename = filename + self.basedir = basedir + self.path = os.path.join(basedir, filename) + self.tree = ElementTree.parse(self.path).getroot() + + description = property(fget=lambda self: self.tree.attrib['description']) + + class RecipeExecutor(Component): commands = ExtensionPoint(ICommandExecutor) reporters = ExtensionPoint(IReportPreparator) - def execute(self, filename='recipe.xml', basedir=os.getcwd()): - path = os.path.join(basedir, filename) - recipe = ElementTree.parse(path).getroot() - for step in recipe: + def execute(self, recipe): + for step in recipe.tree: print '---> %s' % step.attrib['title'] for element in step: if element.tag == 'reports': diff --git a/bitten/tests/__init__.py b/bitten/tests/__init__.py --- a/bitten/tests/__init__.py +++ b/bitten/tests/__init__.py @@ -1,5 +1,8 @@ import unittest +from bitten.tests import recipe + def suite(): suite = unittest.TestSuite() + suite.addTest(recipe.suite()) return suite diff --git a/bitten/tests/recipe.py b/bitten/tests/recipe.py new file mode 100644 --- /dev/null +++ b/bitten/tests/recipe.py @@ -0,0 +1,27 @@ +import os +import os.path +import tempfile +import unittest + +from bitten.recipe import Recipe + + +class RecipeTestCase(unittest.TestCase): + + def setUp(self): + self.temp_dir = tempfile.gettempdir() + self.recipe_xml = open(os.path.join(self.temp_dir, 'recipe.xml'), 'w') + + def tearDown(self): + os.unlink(os.path.join(self.temp_dir, 'recipe.xml')) + + def testDescription(self): + self.recipe_xml.write('' + '' + '') + self.recipe_xml.close() + recipe = Recipe(basedir=self.temp_dir) + self.assertEqual('test', recipe.description) + +def suite(): + return unittest.makeSuite(RecipeTestCase, 'test') diff --git a/scripts/build.py b/scripts/build.py --- a/scripts/build.py +++ b/scripts/build.py @@ -2,11 +2,11 @@ from trac.core import ComponentManager -from bitten.recipe import RecipeExecutor +from bitten.recipe import Recipe, RecipeExecutor from bitten.python import cmd_distutils, rep_pylint from bitten.general import cmd_make if __name__ == '__main__': mgr = ComponentManager() - executor = RecipeExecutor(mgr) - executor.execute() + recipe = Recipe() + RecipeExecutor(mgr).execute(recipe) diff --git a/setup.cfg b/setup.cfg --- a/setup.cfg +++ b/setup.cfg @@ -1,2 +1,5 @@ -[test] +[unittest] test-suite = bitten.tests +xml-results = build/test-results.xml +coverage-results = build/test-coverage.txt +coverage-dir = build/coverage diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -1,6 +1,6 @@ from distutils.core import setup, Command -from bitten.distutils.testrunner import test +from bitten.distutils.testrunner import unittest setup(name='bitten', version='1.0', packages=['bitten', 'bitten.general', 'bitten.python'], - cmdclass={'test': test}) + cmdclass={'unittest': unittest})