runner.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  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, *, durations=None):
  28. """Construct a TextTestResult. Subclasses should accept **kwargs
  29. to ensure compatibility as the interface changes."""
  30. super(TextTestResult, self).__init__(stream, descriptions, verbosity)
  31. self.stream = stream
  32. self.showAll = verbosity > 1
  33. self.dots = verbosity == 1
  34. self.descriptions = descriptions
  35. self._newline = True
  36. self.durations = durations
  37. def getDescription(self, test):
  38. doc_first_line = test.shortDescription()
  39. if self.descriptions and doc_first_line:
  40. return '\n'.join((str(test), doc_first_line))
  41. else:
  42. return str(test)
  43. def startTest(self, test):
  44. super(TextTestResult, self).startTest(test)
  45. if self.showAll:
  46. self.stream.write(self.getDescription(test))
  47. self.stream.write(" ... ")
  48. self.stream.flush()
  49. self._newline = False
  50. def _write_status(self, test, status):
  51. is_subtest = isinstance(test, _SubTest)
  52. if is_subtest or self._newline:
  53. if not self._newline:
  54. self.stream.writeln()
  55. if is_subtest:
  56. self.stream.write(" ")
  57. self.stream.write(self.getDescription(test))
  58. self.stream.write(" ... ")
  59. self.stream.writeln(status)
  60. self.stream.flush()
  61. self._newline = True
  62. def addSubTest(self, test, subtest, err):
  63. if err is not None:
  64. if self.showAll:
  65. if issubclass(err[0], subtest.failureException):
  66. self._write_status(subtest, "FAIL")
  67. else:
  68. self._write_status(subtest, "ERROR")
  69. elif self.dots:
  70. if issubclass(err[0], subtest.failureException):
  71. self.stream.write('F')
  72. else:
  73. self.stream.write('E')
  74. self.stream.flush()
  75. super(TextTestResult, self).addSubTest(test, subtest, err)
  76. def addSuccess(self, test):
  77. super(TextTestResult, self).addSuccess(test)
  78. if self.showAll:
  79. self._write_status(test, "ok")
  80. elif self.dots:
  81. self.stream.write('.')
  82. self.stream.flush()
  83. def addError(self, test, err):
  84. super(TextTestResult, self).addError(test, err)
  85. if self.showAll:
  86. self._write_status(test, "ERROR")
  87. elif self.dots:
  88. self.stream.write('E')
  89. self.stream.flush()
  90. def addFailure(self, test, err):
  91. super(TextTestResult, self).addFailure(test, err)
  92. if self.showAll:
  93. self._write_status(test, "FAIL")
  94. elif self.dots:
  95. self.stream.write('F')
  96. self.stream.flush()
  97. def addSkip(self, test, reason):
  98. super(TextTestResult, self).addSkip(test, reason)
  99. if self.showAll:
  100. self._write_status(test, "skipped {0!r}".format(reason))
  101. elif self.dots:
  102. self.stream.write("s")
  103. self.stream.flush()
  104. def addExpectedFailure(self, test, err):
  105. super(TextTestResult, self).addExpectedFailure(test, err)
  106. if self.showAll:
  107. self.stream.writeln("expected failure")
  108. self.stream.flush()
  109. elif self.dots:
  110. self.stream.write("x")
  111. self.stream.flush()
  112. def addUnexpectedSuccess(self, test):
  113. super(TextTestResult, self).addUnexpectedSuccess(test)
  114. if self.showAll:
  115. self.stream.writeln("unexpected success")
  116. self.stream.flush()
  117. elif self.dots:
  118. self.stream.write("u")
  119. self.stream.flush()
  120. def printErrors(self):
  121. if self.dots or self.showAll:
  122. self.stream.writeln()
  123. self.stream.flush()
  124. self.printErrorList('ERROR', self.errors)
  125. self.printErrorList('FAIL', self.failures)
  126. unexpectedSuccesses = getattr(self, 'unexpectedSuccesses', ())
  127. if unexpectedSuccesses:
  128. self.stream.writeln(self.separator1)
  129. for test in unexpectedSuccesses:
  130. self.stream.writeln(f"UNEXPECTED SUCCESS: {self.getDescription(test)}")
  131. self.stream.flush()
  132. def printErrorList(self, flavour, errors):
  133. for test, err in errors:
  134. self.stream.writeln(self.separator1)
  135. self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
  136. self.stream.writeln(self.separator2)
  137. self.stream.writeln("%s" % err)
  138. self.stream.flush()
  139. class TextTestRunner(object):
  140. """A test runner class that displays results in textual form.
  141. It prints out the names of tests as they are run, errors as they
  142. occur, and a summary of the results at the end of the test run.
  143. """
  144. resultclass = TextTestResult
  145. def __init__(self, stream=None, descriptions=True, verbosity=1,
  146. failfast=False, buffer=False, resultclass=None, warnings=None,
  147. *, tb_locals=False, durations=None):
  148. """Construct a TextTestRunner.
  149. Subclasses should accept **kwargs to ensure compatibility as the
  150. interface changes.
  151. """
  152. if stream is None:
  153. stream = sys.stderr
  154. self.stream = _WritelnDecorator(stream)
  155. self.descriptions = descriptions
  156. self.verbosity = verbosity
  157. self.failfast = failfast
  158. self.buffer = buffer
  159. self.tb_locals = tb_locals
  160. self.durations = durations
  161. self.warnings = warnings
  162. if resultclass is not None:
  163. self.resultclass = resultclass
  164. def _makeResult(self):
  165. try:
  166. return self.resultclass(self.stream, self.descriptions,
  167. self.verbosity, durations=self.durations)
  168. except TypeError:
  169. # didn't accept the durations argument
  170. return self.resultclass(self.stream, self.descriptions,
  171. self.verbosity)
  172. def _printDurations(self, result):
  173. if not result.collectedDurations:
  174. return
  175. ls = sorted(result.collectedDurations, key=lambda x: x[1],
  176. reverse=True)
  177. if self.durations > 0:
  178. ls = ls[:self.durations]
  179. self.stream.writeln("Slowest test durations")
  180. if hasattr(result, 'separator2'):
  181. self.stream.writeln(result.separator2)
  182. hidden = False
  183. for test, elapsed in ls:
  184. if self.verbosity < 2 and elapsed < 0.001:
  185. hidden = True
  186. continue
  187. self.stream.writeln("%-10s %s" % ("%.3fs" % elapsed, test))
  188. if hidden:
  189. self.stream.writeln("\n(durations < 0.001s were hidden; "
  190. "use -v to show these durations)")
  191. else:
  192. self.stream.writeln("")
  193. def run(self, test):
  194. "Run the given test case or test suite."
  195. result = self._makeResult()
  196. registerResult(result)
  197. result.failfast = self.failfast
  198. result.buffer = self.buffer
  199. result.tb_locals = self.tb_locals
  200. with warnings.catch_warnings():
  201. if self.warnings:
  202. # if self.warnings is set, use it to filter all the warnings
  203. warnings.simplefilter(self.warnings)
  204. startTime = time.perf_counter()
  205. startTestRun = getattr(result, 'startTestRun', None)
  206. if startTestRun is not None:
  207. startTestRun()
  208. try:
  209. test(result)
  210. finally:
  211. stopTestRun = getattr(result, 'stopTestRun', None)
  212. if stopTestRun is not None:
  213. stopTestRun()
  214. stopTime = time.perf_counter()
  215. timeTaken = stopTime - startTime
  216. result.printErrors()
  217. if self.durations is not None:
  218. self._printDurations(result)
  219. if hasattr(result, 'separator2'):
  220. self.stream.writeln(result.separator2)
  221. run = result.testsRun
  222. self.stream.writeln("Ran %d test%s in %.3fs" %
  223. (run, run != 1 and "s" or "", timeTaken))
  224. self.stream.writeln()
  225. expectedFails = unexpectedSuccesses = skipped = 0
  226. try:
  227. results = map(len, (result.expectedFailures,
  228. result.unexpectedSuccesses,
  229. result.skipped))
  230. except AttributeError:
  231. pass
  232. else:
  233. expectedFails, unexpectedSuccesses, skipped = results
  234. infos = []
  235. if not result.wasSuccessful():
  236. self.stream.write("FAILED")
  237. failed, errored = len(result.failures), len(result.errors)
  238. if failed:
  239. infos.append("failures=%d" % failed)
  240. if errored:
  241. infos.append("errors=%d" % errored)
  242. elif run == 0:
  243. self.stream.write("NO TESTS RAN")
  244. else:
  245. self.stream.write("OK")
  246. if skipped:
  247. infos.append("skipped=%d" % skipped)
  248. if expectedFails:
  249. infos.append("expected failures=%d" % expectedFails)
  250. if unexpectedSuccesses:
  251. infos.append("unexpected successes=%d" % unexpectedSuccesses)
  252. if infos:
  253. self.stream.writeln(" (%s)" % (", ".join(infos),))
  254. else:
  255. self.stream.write("\n")
  256. self.stream.flush()
  257. return result