runner.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. import os
  2. import sys
  3. import time
  4. import unittest
  5. class TextTestRunner(unittest.TextTestRunner):
  6. """This is a wrapper class used to minimize the amount of blank lines
  7. printed to stdout. The method ``run`` is modified and it's original source
  8. is in Python Standard Library's ``unittest`` module."""
  9. def __init__(self, stream=sys.stderr, descriptions=True, verbosity=1,
  10. color=True):
  11. unittest.TextTestRunner.__init__(self, stream, descriptions, verbosity)
  12. self.color = color
  13. def run(self, test):
  14. "Run the given test case or test suite."
  15. result = self._makeResult()
  16. result.failfast = getattr(self, 'failfast', None)
  17. result.buffer = getattr(self, 'buffer', None)
  18. startTime = time.time()
  19. startTestRun = getattr(result, 'startTestRun', None)
  20. if startTestRun is not None:
  21. startTestRun()
  22. try:
  23. test(result)
  24. finally:
  25. stopTestRun = getattr(result, 'stopTestRun', None)
  26. if stopTestRun is not None:
  27. stopTestRun()
  28. stopTime = time.time()
  29. timeTaken = stopTime - startTime
  30. result.printErrors()
  31. run = result.testsRun
  32. msg = 'Ran %d test%s in %.3fs '
  33. self.stream.write(msg % (run, run != 1 and 's' or '', timeTaken))
  34. expectedFails = unexpectedSuccesses = skipped = 0
  35. try:
  36. results = map(len, (result.expectedFailures,
  37. result.unexpectedSuccesses,
  38. result.skipped))
  39. except AttributeError:
  40. pass
  41. else:
  42. expectedFails, unexpectedSuccesses, skipped = results
  43. infos = []
  44. if not result.wasSuccessful():
  45. if self.color:
  46. msg = '\033[0;31mFAILED\033[1;m'
  47. else:
  48. msg = 'FAILED'
  49. self.stream.write(msg)
  50. failed, errored = map(len, (result.failures, result.errors))
  51. if failed:
  52. if self.color:
  53. msg = 'failures=\033[1;31m%d\033[1;m'
  54. else:
  55. msg = 'failures=%d'
  56. infos.append(msg % failed)
  57. if errored:
  58. if self.color:
  59. msg = 'errors=\033[1;31m%d\033[1;m'
  60. else:
  61. msg = 'errors=%d'
  62. infos.append(msg % errored)
  63. else:
  64. if self.color:
  65. msg = '\033[0;32mOK\033[0;m'
  66. else:
  67. msg = 'OK'
  68. self.stream.write(msg)
  69. if skipped:
  70. if self.color:
  71. msg = 'skipped=\033[1;33m%d\033[1;m'
  72. else:
  73. msg = 'skipped=%d'
  74. infos.append(msg % skipped)
  75. if expectedFails:
  76. infos.append('expected failures=%d' % expectedFails)
  77. if unexpectedSuccesses:
  78. infos.append('unexpected successes=%d' % unexpectedSuccesses)
  79. if infos:
  80. self.stream.writeln(' (%s)' % (', '.join(infos),))
  81. else:
  82. self.stream.write('\n')
  83. return result
  84. def main(tests, verbose=0, color=True):
  85. suites = []
  86. # Dynamic load the requested module containing the test cases.
  87. for testfile in tests:
  88. module_dir, module_file = os.path.split(testfile)
  89. module_name, module_ext = os.path.splitext(module_file)
  90. # XXX: this looks really hacky, so cleanup is necessary.
  91. module_obj = __import__(module_dir.replace('/', '.'),
  92. fromlist=[module_name])
  93. module_obj.__file__ = testfile
  94. globals()[module_name] = module_obj
  95. # Start the test runner and display the results to stdout.
  96. container = module_obj.__dict__[module_name]
  97. class_parts = module_name[5:].split('_')
  98. class_name = 'Test' + ''.join([p.capitalize() for p in class_parts])
  99. testcase = container.__dict__[class_name]
  100. suites += [unittest.TestLoader().loadTestsFromTestCase(testcase)]
  101. # Create the text runner and execute the tests.
  102. runner = TextTestRunner(verbosity=verbose, color=color)
  103. runner.run(unittest.TestSuite(suites))