From 58e1eeffb7324476443d7f4e7d87591854498786 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Mon, 18 Feb 2013 17:46:52 +0000 Subject: Add runner class for handling running unittests specified on the commandline through the options parser, discovering unittest cases within whatever file/module/class/method/package is passed to the runner, and calling twisted.trial on the unittest cases found. --- src/leap/mx/runner.py | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/src/leap/mx/runner.py b/src/leap/mx/runner.py index daf956e..84db927 100644 --- a/src/leap/mx/runner.py +++ b/src/leap/mx/runner.py @@ -13,6 +13,12 @@ from os import path as ospath import re +from twisted.internet import defer +from twisted.trial import runner, reporter, unittest +from twisted.python import usage + +from leap.mx.util import log, version + class CheckRequirements(ImportError): """ @@ -80,4 +86,119 @@ class CheckRequirements(ImportError): package_name = matched.group() package_version = shortened.split(package_name, 1)[1] dependencies.append((package_name, package_version)) + return dependencies + + +class TestRunner(runner.TrialRunner): + """ + This class handles loading unittests from a directory, module, file, + class, method, or anything really, and running any unittests found with + twisted.trial. + + @param options: A subclass of :class:twisted.python.usage.Options. + @param test: (optional) The thing to load tests from. + """ + def __init__(self, options, test_location=None, *args, **kwargs): + """ + Create a runner for handling unittest runs. + + @param options: An the dictionary :ivar:`opts` from the parsed options + of a :class:`twisted.python.usage.Options`. + @param test_location: The path to a directory or filename containing + unittests to run, or the path to a file prefixed with 'test_', or + a subclass of :class:`twisted.trial.unittest.TestCase`, or a + function/method prefixed with 'test_'. + """ + log.debug("Creating TestRunner: %s" % self.__repr__()) + + if isinstance(options, dict): + log.debug("TestRunner loaded options class") + self.options = options + else: + self.options = None + raise usage.UsageError( + "TestRunner expected t.p.u.Options subclass, got %s" + % options) + + self.loader = runner.TestLoader() + self._parse_options() + + self.test_location = test_location + self.tests_loaded = self.loadUnittests() + self.test_list = self.runUnittests() + self.results = defer.DeferredList(self.test_list) + + def _parse_options(self): + """ + Parse the :class:`twisted.python.usage.Options` for flags pertaining + to running unittests. + """ + if not self.options is None: + if self.options['debug']: + log.debug("Enabled debugging on test runner.") + self.DEBUG = True + if self.options['force-gc']: + ## not sure if we need to call it or assign it... + log.debug("Forcing garbage collection between unittest runs.") + self.loader.forceGarbageCollection(True) + if self.options['all-tests']: + repo = version.getRepoDir() + test_directory = ospath.join(repo, 'src/leap/mx/tests') + self.test_location = test_directory + else: + log.warn("TestRunner: got None for t.p.u.Options class!") + + def loadUnittests(self): + """ + Load all tests. Tests may be a module or directory, the path to a + filename in the form 'test_*.py", a subclass of + :class:`twisted.trial.unittest.TestCase` or + :class:`twisted.trial.unittest.TestSuite`, or a any function/method + which is prefixed with 'test_'. + + @returns: An instance of :class:`twisted.trial.unittest.TestCase` or + :class:`twisted.trial.unittest.TestSuite`. + """ + log.msg("Attempting to load unittests...") + + tests_loaded = None + + if self.test_location: + log.msg("Loading unittests from %s" % self.test_location) + if ospath.isdir(self.test_location): + log.msg("Found test directory: %s" % test) + tests_loaded = self.loader.loadAnything(self.test_location, + recurse=True) + else: + log.msg("Found test file: %s" % self.test_location) + tests_loaded = self.loader.loadAnything(self.test_location) + else: + log.warn("Test location %s seems to be None!" % self.test_location) + + return tests_loaded + + def runUnittests(self): + """xxx fill me in""" + results = [] + if not self.tests_loaded is None: + if isinstance(self.tests_loaded, unittest.TestCase): + log.msg("Test case loaded.") + classes = self.loader.findTestClasses(self.tests_loaded) + for cls in classes: + test_instance = cls() + test_instance.setUp() ## xxx does TestRunner handle this? + d = defer.maybeDeferred(test_instance.run()) + self.results.append(d) + elif isinstance(self.tests, unittest.TestSuite): + classes = None ## xxx call each TestCase in TestSuite + test_suite = self.tests() + self.results.append(test_suite.visit()) + log.msg("Test suite loaded: %d tests to run" + % test_suite.countTestCases) + return results + + #return runner.TrialRunner(reporter.TreeReporter, mode=mode, + # profile=profile, logfile=logfile, + # tbformat, rterrors, unclean_warnings, + # temp-directory, force-gc) -- cgit v1.2.3