runner.py 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. """Running tests"""
  2. import sys
  3. import time
  4. import warnings
  5. from . import result
  6. from .case import _SubTest
  7. from .signals import registerResult
  8. __unittest = True
  9. class _WritelnDecorator(object):
  10. """Used to decorate file-like objects with a handy 'writeln' method"""
  11. def __init__(self,stream):
  12. self.stream = stream
  13. def __getattr__(self, attr):
  14. if attr in ('stream', '__getstate__'):
  15. raise AttributeError(attr)
  16. return getattr(self.stream,attr)
  17. def writeln(self, arg=None):
  18. if arg:
  19. self.write(arg)
  20. self.write('\n') # text-mode streams translate to \r\n if needed
  21. class TextTestResult(result.TestResult):
  22. """A test result class that can print formatted text results to a stream.
  23. Used by TextTestRunner.
  24. """
  25. separator1 = '=' * 70
  26. separator2 = '-' * 70
  27. def __init__(self, stream, descriptions, verbosity):
  28. super(TextTestResult, self).__init__(stream, descriptions, verbosity)
  29. self.stream = stream
  30. self.showAll = verbosity > 1
  31. self.dots = verbosity == 1
  32. self.descriptions = descriptions
  33. self._newline = True
  34. def getDescription(self, test):
  35. doc_first_line = test.shortDescription()
  36. if self.descriptions and doc_first_line:
  37. return '\n'.join((str(test), doc_first_line))
  38. else:
  39. return str(test)
  40. def startTest(self, test):
  41. super(TextTestResult, self).startTest(test)
  42. if self.showAll:
  43. self.stream.write(self.getDescription(test))
  44. self.stream.write(" ... ")
  45. self.stream.flush()
  46. self._newline = False
  47. def _write_status(self, test, status):
  48. is_subtest = isinstance(test, _SubTest)
  49. if is_subtest or self._newline:
  50. if not self._newline:
  51. self.stream.writeln()
  52. if is_subtest:
  53. self.stream.write(" ")
  54. self.stream.write(self.getDescription(test))
  55. self.stream.write(" ... ")
  56. self.stream.writeln(status)
  57. self.stream.flush()
  58. self._newline = True
  59. def addSubTest(self, test, subtest, err):
  60. if err is not None:
  61. if self.showAll:
  62. if issubclass(err[0], subtest.failureException):
  63. self._write_status(subtest, "FAIL")
  64. else:
  65. self._write_status(subtest, "ERROR")
  66. elif self.dots:
  67. if issubclass(err[0], subtest.failureException):
  68. self.stream.write('F')
  69. else:
  70. self.stream.write('E')
  71. self.stream.flush()
  72. super(TextTestResult, self).addSubTest(test, subtest, err)
  73. def addSuccess(self, test):
  74. super(TextTestResult, self).addSuccess(test)
  75. if self.showAll:
  76. self._write_status(test, "ok")
  77. elif self.dots:
  78. self.stream.write('.')
  79. self.stream.flush()
  80. def addError(self, test, err):
  81. super(TextTestResult, self).addError(test, err)
  82. if self.showAll:
  83. self._write_status(test, "ERROR")
  84. elif self.dots:
  85. self.stream.write('E')
  86. self.stream.flush()
  87. def addFailure(self, test, err):
  88. super(TextTestResult, self).addFailure(test, err)
  89. if self.showAll:
  90. self._write_status(test, "FAIL")
  91. elif self.dots:
  92. self.stream.write('F')
  93. self.stream.flush()
  94. def addSkip(self, test, reason):
  95. super(TextTestResult, self).addSkip(test, reason)
  96. if self.showAll:
  97. self._write_status(test, "skipped {0!r}".format(reason))
  98. elif self.dots:
  99. self.stream.write("s")
  100. self.stream.flush()
  101. def addExpectedFailure(self, test, err):
  102. super(TextTestResult, self).addExpectedFailure(test, err)
  103. if self.showAll:
  104. self.stream.writeln("expected failure")
  105. self.stream.flush()
  106. elif self.dots:
  107. self.stream.write("x")
  108. self.stream.flush()
  109. def addUnexpectedSuccess(self, test):
  110. super(TextTestResult, self).addUnexpectedSuccess(test)
  111. if self.showAll:
  112. self.stream.writeln("unexpected success")
  113. self.stream.flush()
  114. elif self.dots:
  115. self.stream.write("u")
  116. self.stream.flush()
  117. def printErrors(self):
  118. if self.dots or self.showAll:
  119. self.stream.writeln()
  120. self.stream.flush()
  121. self.printErrorList('ERROR', self.errors)
  122. self.printErrorList('FAIL', self.failures)
  123. unexpectedSuccesses = getattr(self, 'unexpectedSuccesses', ())
  124. if unexpectedSuccesses:
  125. self.stream.writeln(self.separator1)
  126. for test in unexpectedSuccesses:
  127. self.stream.writeln(f"UNEXPECTED SUCCESS: {self.getDescription(test)}")
  128. self.stream.flush()
  129. def printErrorList(self, flavour, errors):
  130. for test, err in errors:
  131. self.stream.writeln(self.separator1)
  132. self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
  133. self.stream.writeln(self.separator2)
  134. self.stream.writeln("%s" % err)
  135. self.stream.flush()
  136. class TextTestRunner(object):
  137. """A test runner class that displays results in textual form.
  138. It prints out the names of tests as they are run, errors as they
  139. occur, and a summary of the results at the end of the test run.
  140. """
  141. resultclass = TextTestResult
  142. def __init__(self, stream=None, descriptions=True, verbosity=1,
  143. failfast=False, buffer=False, resultclass=None, warnings=None,
  144. *, tb_locals=False):
  145. """Construct a TextTestRunner.
  146. Subclasses should accept **kwargs to ensure compatibility as the
  147. interface changes.
  148. """
  149. if stream is None:
  150. stream = sys.stderr
  151. self.stream = _WritelnDecorator(stream)
  152. self.descriptions = descriptions
  153. self.verbosity = verbosity
  154. self.failfast = failfast
  155. self.buffer = buffer
  156. self.tb_locals = tb_locals
  157. self.warnings = warnings
  158. if resultclass is not None:
  159. self.resultclass = resultclass
  160. def _makeResult(self):
  161. return self.resultclass(self.stream, self.descriptions, self.verbosity)
  162. def run(self, test):
  163. "Run the given test case or test suite."
  164. result = self._makeResult()
  165. registerResult(result)
  166. result.failfast = self.failfast
  167. result.buffer = self.buffer
  168. result.tb_locals = self.tb_locals
  169. with warnings.catch_warnings():
  170. if self.warnings:
  171. # if self.warnings is set, use it to filter all the warnings
  172. warnings.simplefilter(self.warnings)
  173. # if the filter is 'default' or 'always', special-case the
  174. # warnings from the deprecated unittest methods to show them
  175. # no more than once per module, because they can be fairly
  176. # noisy. The -Wd and -Wa flags can be used to bypass this
  177. # only when self.warnings is None.
  178. if self.warnings in ['default', 'always']:
  179. warnings.filterwarnings('module',
  180. category=DeprecationWarning,
  181. message=r'Please use assert\w+ instead.')
  182. startTime = time.perf_counter()
  183. startTestRun = getattr(result, 'startTestRun', None)
  184. if startTestRun is not None:
  185. startTestRun()
  186. try:
  187. test(result)
  188. finally:
  189. stopTestRun = getattr(result, 'stopTestRun', None)
  190. if stopTestRun is not None:
  191. stopTestRun()
  192. stopTime = time.perf_counter()
  193. timeTaken = stopTime - startTime
  194. result.printErrors()
  195. if hasattr(result, 'separator2'):
  196. self.stream.writeln(result.separator2)
  197. run = result.testsRun
  198. self.stream.writeln("Ran %d test%s in %.3fs" %
  199. (run, run != 1 and "s" or "", timeTaken))
  200. self.stream.writeln()
  201. expectedFails = unexpectedSuccesses = skipped = 0
  202. try:
  203. results = map(len, (result.expectedFailures,
  204. result.unexpectedSuccesses,
  205. result.skipped))
  206. except AttributeError:
  207. pass
  208. else:
  209. expectedFails, unexpectedSuccesses, skipped = results
  210. infos = []
  211. if not result.wasSuccessful():
  212. self.stream.write("FAILED")
  213. failed, errored = len(result.failures), len(result.errors)
  214. if failed:
  215. infos.append("failures=%d" % failed)
  216. if errored:
  217. infos.append("errors=%d" % errored)
  218. else:
  219. self.stream.write("OK")
  220. if skipped:
  221. infos.append("skipped=%d" % skipped)
  222. if expectedFails:
  223. infos.append("expected failures=%d" % expectedFails)
  224. if unexpectedSuccesses:
  225. infos.append("unexpected successes=%d" % unexpectedSuccesses)
  226. if infos:
  227. self.stream.writeln(" (%s)" % (", ".join(infos),))
  228. else:
  229. self.stream.write("\n")
  230. self.stream.flush()
  231. return result