# # Python unit test runner # # Author : Sander Mathijs van Veen # License: GPL version 3, see also the file `LICENSE'. # import os import sys import time import unittest class TextTestRunner(unittest.TextTestRunner): """This is a wrapper class used to minimize the amount of blank lines printed to stdout. The method ``run`` is modified and it's original source is in Python Standard Library's ``unittest`` module.""" def __init__(self, stream=sys.stderr, descriptions=True, verbosity=1, color=True): unittest.TextTestRunner.__init__(self, stream, descriptions, verbosity) self.color = color def run(self, test): "Run the given test case or test suite." result = self._makeResult() result.failfast = getattr(self, 'failfast', None) result.buffer = getattr(self, 'buffer', None) startTime = time.time() startTestRun = getattr(result, 'startTestRun', None) if startTestRun is not None: startTestRun() try: test(result) finally: stopTestRun = getattr(result, 'stopTestRun', None) if stopTestRun is not None: stopTestRun() stopTime = time.time() timeTaken = stopTime - startTime result.printErrors() if not result.wasSuccessful(): if self.color: msg = '\033[0;31mFAIL\033[1;m' else: msg = 'FAIL' self.stream.write(msg) else: if self.color: msg = '\033[0;32mOK\033[0;m' else: msg = 'OK' self.stream.write(msg) self.stream.write(' %s: %d test%s in %.3fs ' \ % (test.filename, result.testsRun, result.testsRun != 1 and 's' or '', timeTaken)) expectedFails = unexpectedSuccesses = skipped = 0 try: results = map(len, (result.expectedFailures, result.unexpectedSuccesses, result.skipped)) except AttributeError: pass else: expectedFails, unexpectedSuccesses, skipped = results infos = [] if not result.wasSuccessful(): failed, errored = map(len, (result.failures, result.errors)) if failed: if self.color: msg = 'failures=\033[1;31m%d\033[1;m' else: msg = 'failures=%d' infos.append(msg % failed) if errored: if self.color: msg = 'errors=\033[1;31m%d\033[1;m' else: msg = 'errors=%d' infos.append(msg % errored) if skipped: if self.color: msg = 'skipped=\033[1;33m%d\033[1;m' else: msg = 'skipped=%d' infos.append(msg % skipped) if expectedFails: infos.append('expected failures=%d' % expectedFails) if unexpectedSuccesses: infos.append('unexpected successes=%d' % unexpectedSuccesses) if infos: self.stream.writeln(' (%s)' % (', '.join(infos),)) else: self.stream.write('\n') return result def main(tests, verbose=0, color=True): testcases = [] # Dynamic load the requested module containing the test cases. for testfile in tests: try: module_name = os.path.splitext(testfile)[0].replace('/', '.') module_obj = __import__(module_name) except: print 'testfile: ', testfile print 'module_name:', module_name raise # Start the test runner and display the results to stdout. try: suite_name = module_name.split('.')[-1] container = module_obj.__dict__[suite_name] except: print 'testfile: ', testfile print 'module_name:', module_name print 'module_obj: ', module_obj, dir(module_obj) raise # Convert lowercase, underscored suite name to Python class name. class_parts = suite_name[5:].split('_') class_name = 'Test' + ''.join([p.capitalize() for p in class_parts]) testcase = container.__dict__[class_name] testcases += [unittest.TestLoader().loadTestsFromTestCase(testcase)] # Create the text runner and execute the tests. runner = TextTestRunner(verbosity=verbose, color=color) suite = unittest.TestSuite(testcases) suite.filename = testfile if tests else None result = runner.run(suite) # Return non zero exit code if there are failures or errors occured. if result.failures or result.errors: sys.exit(1)