import argparse import sys import time import six from .unittest import TextTestRunner, TestProgram from .result import _XMLTestResult # see issue #74, the encoding name needs to be one of # http://www.iana.org/assignments/character-sets/character-sets.xhtml UTF8 = 'UTF-8' class XMLTestRunner(TextTestRunner): """ A test runner class that outputs the results in JUnit like XML files. """ def __init__(self, output='.', outsuffix=None, elapsed_times=True, encoding=UTF8, resultclass=None, warnings=None, **kwargs): if six.PY3: kwargs['warnings'] = warnings super(XMLTestRunner, self).__init__(**kwargs) self.output = output self.encoding = encoding # None means default timestamped suffix # '' (empty) means no suffix if outsuffix is None: outsuffix = time.strftime("%Y%m%d%H%M%S") self.outsuffix = outsuffix self.elapsed_times = elapsed_times if resultclass is None: self.resultclass = _XMLTestResult else: self.resultclass = resultclass def _make_result(self): """ Creates a TestResult object which will be used to store information about the executed tests. """ # override in subclasses if necessary. return self.resultclass( self.stream, self.descriptions, self.verbosity, self.elapsed_times ) def run(self, test): """ Runs the given test case or test suite. """ try: # Prepare the test execution result = self._make_result() result.failfast = self.failfast result.buffer = self.buffer if hasattr(test, 'properties'): # junit testsuite properties result.properties = test.properties # Print a nice header self.stream.writeln() self.stream.writeln('Running tests...') self.stream.writeln(result.separator2) # Execute tests start_time = time.time() test(result) stop_time = time.time() time_taken = stop_time - start_time # Print results result.printErrors() self.stream.writeln(result.separator2) run = result.testsRun self.stream.writeln("Ran %d test%s in %.3fs" % ( run, run != 1 and "s" or "", time_taken) ) self.stream.writeln() # other metrics expectedFails = len(result.expectedFailures) unexpectedSuccesses = len(result.unexpectedSuccesses) skipped = len(result.skipped) # Error traces infos = [] if not result.wasSuccessful(): self.stream.write("FAILED") failed, errored = map(len, (result.failures, result.errors)) if failed: infos.append("failures={0}".format(failed)) if errored: infos.append("errors={0}".format(errored)) else: self.stream.write("OK") if skipped: infos.append("skipped={0}".format(skipped)) if expectedFails: infos.append("expected failures={0}".format(expectedFails)) if unexpectedSuccesses: infos.append("unexpected successes={0}".format( unexpectedSuccesses)) if infos: self.stream.writeln(" ({0})".format(", ".join(infos))) else: self.stream.write("\n") # Generate reports self.stream.writeln() self.stream.writeln('Generating XML reports...') result.generate_reports(self) finally: pass return result class XMLTestProgram(TestProgram): def __init__(self, *args, **kwargs): kwargs.setdefault('testRunner', XMLTestRunner) self.warnings = None # python2 fix self._parseKnownArgs(kwargs) super(XMLTestProgram, self).__init__(*args, **kwargs) def _parseKnownArgs(self, kwargs): argv = kwargs.get('argv') if argv is None: argv = sys.argv # python2 argparse fix parser = argparse.ArgumentParser(prog='xmlrunner') group = parser.add_mutually_exclusive_group() group.add_argument( '-o', '--output', metavar='DIR', help='Directory for storing XML reports (\'.\' default)') group.add_argument( '--output-file', metavar='FILENAME', help='Filename for storing XML report') namespace, argv = parser.parse_known_args(argv) self.output = namespace.output self.output_file = namespace.output_file kwargs['argv'] = argv def _initArgParsers(self): # this code path is only called in python3 (optparse vs argparse) super(XMLTestProgram, self)._initArgParsers() for parser in (self._main_parser, self._discovery_parser): group = parser.add_mutually_exclusive_group() group.add_argument( '-o', '--output', metavar='DIR', nargs=1, help='Directory for storing XML reports (\'.\' default)') group.add_argument( '--output-file', metavar='FILENAME', nargs=1, help='Filename for storing XML report') def runTests(self): kwargs = dict( verbosity=self.verbosity, failfast=self.failfast, buffer=self.buffer, warnings=self.warnings, ) if sys.version_info[:2] > (3, 4): kwargs.update(tb_locals=self.tb_locals) output_file = None try: if self.output_file is not None: output_file = open(self.output_file, 'wb') kwargs.update(output=output_file) elif self.output is not None: kwargs.update(output=self.output) self.testRunner = self.testRunner(**kwargs) super(XMLTestProgram, self).runTests() finally: if output_file is not None: output_file.close()