test_runner.py 33 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. #
  4. # Maintainer: Jonathan Lange
  5. # Author: Robert Collins
  6. from __future__ import absolute_import, division
  7. import os
  8. import pdb
  9. import sys
  10. from zope.interface import implementer
  11. from zope.interface.verify import verifyObject
  12. from twisted.trial.itrial import IReporter, ITestCase
  13. from twisted.trial import unittest, runner, reporter, util
  14. from twisted.trial._asyncrunner import _ForceGarbageCollectionDecorator
  15. from twisted.python import failure, log, reflect
  16. from twisted.python.filepath import FilePath
  17. from twisted.python.reflect import namedAny
  18. from twisted.python.compat import NativeStringIO
  19. from twisted.scripts import trial
  20. from twisted.plugins import twisted_trial
  21. from twisted import plugin
  22. from twisted.internet import defer
  23. pyunit = __import__('unittest')
  24. class CapturingDebugger(object):
  25. def __init__(self):
  26. self._calls = []
  27. def runcall(self, *args, **kwargs):
  28. self._calls.append('runcall')
  29. args[0](*args[1:], **kwargs)
  30. @implementer(IReporter)
  31. class CapturingReporter(object):
  32. """
  33. Reporter that keeps a log of all actions performed on it.
  34. """
  35. stream = None
  36. tbformat = None
  37. args = None
  38. separator = None
  39. testsRun = None
  40. def __init__(self, stream=None, tbformat=None, rterrors=None,
  41. publisher=None):
  42. """
  43. Create a capturing reporter.
  44. """
  45. self._calls = []
  46. self.shouldStop = False
  47. self._stream = stream
  48. self._tbformat = tbformat
  49. self._rterrors = rterrors
  50. self._publisher = publisher
  51. def startTest(self, method):
  52. """
  53. Report the beginning of a run of a single test method
  54. @param method: an object that is adaptable to ITestMethod
  55. """
  56. self._calls.append('startTest')
  57. def stopTest(self, method):
  58. """
  59. Report the status of a single test method
  60. @param method: an object that is adaptable to ITestMethod
  61. """
  62. self._calls.append('stopTest')
  63. def cleanupErrors(self, errs):
  64. """called when the reactor has been left in a 'dirty' state
  65. @param errs: a list of L{twisted.python.failure.Failure}s
  66. """
  67. self._calls.append('cleanupError')
  68. def addSuccess(self, test):
  69. self._calls.append('addSuccess')
  70. def done(self):
  71. """
  72. Do nothing. These tests don't care about done.
  73. """
  74. class TrialRunnerTestsMixin(object):
  75. """
  76. Mixin defining tests for L{runner.TrialRunner}.
  77. """
  78. def tearDown(self):
  79. self.runner._tearDownLogFile()
  80. def test_empty(self):
  81. """
  82. Empty test method, used by the other tests.
  83. """
  84. def _getObservers(self):
  85. return log.theLogPublisher.observers
  86. def test_addObservers(self):
  87. """
  88. Any log system observers L{TrialRunner.run} adds are removed by the
  89. time it returns.
  90. """
  91. originalCount = len(self._getObservers())
  92. self.runner.run(self.test)
  93. newCount = len(self._getObservers())
  94. self.assertEqual(newCount, originalCount)
  95. def test_logFileAlwaysActive(self):
  96. """
  97. Test that a new file is opened on each run.
  98. """
  99. oldSetUpLogFile = self.runner._setUpLogFile
  100. l = []
  101. def setUpLogFile():
  102. oldSetUpLogFile()
  103. l.append(self.runner._logFileObserver)
  104. self.runner._setUpLogFile = setUpLogFile
  105. self.runner.run(self.test)
  106. self.runner.run(self.test)
  107. self.assertEqual(len(l), 2)
  108. self.assertFalse(l[0] is l[1], "Should have created a new file observer")
  109. def test_logFileGetsClosed(self):
  110. """
  111. Test that file created is closed during the run.
  112. """
  113. oldSetUpLogFile = self.runner._setUpLogFile
  114. l = []
  115. def setUpLogFile():
  116. oldSetUpLogFile()
  117. l.append(self.runner._logFileObject)
  118. self.runner._setUpLogFile = setUpLogFile
  119. self.runner.run(self.test)
  120. self.assertEqual(len(l), 1)
  121. self.assertTrue(l[0].closed)
  122. class TrialRunnerTests(TrialRunnerTestsMixin, unittest.SynchronousTestCase):
  123. """
  124. Tests for L{runner.TrialRunner} with the feature to turn unclean errors
  125. into warnings disabled.
  126. """
  127. def setUp(self):
  128. self.stream = NativeStringIO()
  129. self.runner = runner.TrialRunner(CapturingReporter, stream=self.stream)
  130. self.test = TrialRunnerTests('test_empty')
  131. def test_publisher(self):
  132. """
  133. The reporter constructed by L{runner.TrialRunner} is passed
  134. L{twisted.python.log} as the value for the C{publisher} parameter.
  135. """
  136. result = self.runner._makeResult()
  137. self.assertIdentical(result._publisher, log)
  138. class TrialRunnerWithUncleanWarningsReporterTests(TrialRunnerTestsMixin,
  139. unittest.SynchronousTestCase):
  140. """
  141. Tests for the TrialRunner's interaction with an unclean-error suppressing
  142. reporter.
  143. """
  144. def setUp(self):
  145. self.stream = NativeStringIO()
  146. self.runner = runner.TrialRunner(CapturingReporter, stream=self.stream,
  147. uncleanWarnings=True)
  148. self.test = TrialRunnerTests('test_empty')
  149. class DryRunMixin(object):
  150. """
  151. Mixin for testing that 'dry run' mode works with various
  152. L{pyunit.TestCase} subclasses.
  153. """
  154. def setUp(self):
  155. self.log = []
  156. self.stream = NativeStringIO()
  157. self.runner = runner.TrialRunner(CapturingReporter,
  158. runner.TrialRunner.DRY_RUN,
  159. stream=self.stream)
  160. self.makeTestFixtures()
  161. def makeTestFixtures(self):
  162. """
  163. Set C{self.test} and C{self.suite}, where C{self.suite} is an empty
  164. TestSuite.
  165. """
  166. def test_empty(self):
  167. """
  168. If there are no tests, the reporter should not receive any events to
  169. report.
  170. """
  171. result = self.runner.run(runner.TestSuite())
  172. self.assertEqual(result._calls, [])
  173. def test_singleCaseReporting(self):
  174. """
  175. If we are running a single test, check the reporter starts, passes and
  176. then stops the test during a dry run.
  177. """
  178. result = self.runner.run(self.test)
  179. self.assertEqual(result._calls, ['startTest', 'addSuccess', 'stopTest'])
  180. def test_testsNotRun(self):
  181. """
  182. When we are doing a dry run, the tests should not actually be run.
  183. """
  184. self.runner.run(self.test)
  185. self.assertEqual(self.log, [])
  186. class SynchronousDryRunTests(DryRunMixin, unittest.SynchronousTestCase):
  187. """
  188. Check that 'dry run' mode works well with trial's L{SynchronousTestCase}.
  189. """
  190. def makeTestFixtures(self):
  191. class PyunitCase(unittest.SynchronousTestCase):
  192. def test_foo(self):
  193. pass
  194. self.test = PyunitCase('test_foo')
  195. self.suite = pyunit.TestSuite()
  196. class DryRunTests(DryRunMixin, unittest.SynchronousTestCase):
  197. """
  198. Check that 'dry run' mode works well with Trial tests.
  199. """
  200. def makeTestFixtures(self):
  201. class MockTest(unittest.TestCase):
  202. def test_foo(test):
  203. self.log.append('test_foo')
  204. self.test = MockTest('test_foo')
  205. self.suite = runner.TestSuite()
  206. class PyUnitDryRunTests(DryRunMixin, unittest.SynchronousTestCase):
  207. """
  208. Check that 'dry run' mode works well with stdlib unittest tests.
  209. """
  210. def makeTestFixtures(self):
  211. class PyunitCase(pyunit.TestCase):
  212. def test_foo(self):
  213. pass
  214. self.test = PyunitCase('test_foo')
  215. self.suite = pyunit.TestSuite()
  216. class RunnerTests(unittest.SynchronousTestCase):
  217. def setUp(self):
  218. self.config = trial.Options()
  219. # whitebox hack a reporter in, because plugins are CACHED and will
  220. # only reload if the FILE gets changed.
  221. parts = reflect.qual(CapturingReporter).split('.')
  222. package = '.'.join(parts[:-1])
  223. klass = parts[-1]
  224. plugins = [twisted_trial._Reporter(
  225. "Test Helper Reporter",
  226. package,
  227. description="Utility for unit testing.",
  228. longOpt="capturing",
  229. shortOpt=None,
  230. klass=klass)]
  231. # XXX There should really be a general way to hook the plugin system
  232. # for tests.
  233. def getPlugins(iface, *a, **kw):
  234. self.assertEqual(iface, IReporter)
  235. return plugins + list(self.original(iface, *a, **kw))
  236. self.original = plugin.getPlugins
  237. plugin.getPlugins = getPlugins
  238. self.standardReport = ['startTest', 'addSuccess', 'stopTest',
  239. 'startTest', 'addSuccess', 'stopTest',
  240. 'startTest', 'addSuccess', 'stopTest',
  241. 'startTest', 'addSuccess', 'stopTest',
  242. 'startTest', 'addSuccess', 'stopTest',
  243. 'startTest', 'addSuccess', 'stopTest',
  244. 'startTest', 'addSuccess', 'stopTest',
  245. 'startTest', 'addSuccess', 'stopTest',
  246. 'startTest', 'addSuccess', 'stopTest',
  247. 'startTest', 'addSuccess', 'stopTest']
  248. def tearDown(self):
  249. plugin.getPlugins = self.original
  250. def parseOptions(self, args):
  251. self.config.parseOptions(args)
  252. def getRunner(self):
  253. r = trial._makeRunner(self.config)
  254. r.stream = NativeStringIO()
  255. # XXX The runner should always take care of cleaning this up itself.
  256. # It's not clear why this is necessary. The runner always tears down
  257. # its log file.
  258. self.addCleanup(r._tearDownLogFile)
  259. # XXX The runner should always take care of cleaning this up itself as
  260. # well. It's necessary because TrialRunner._setUpTestdir might raise
  261. # an exception preventing Reporter.done from being run, leaving the
  262. # observer added by Reporter.__init__ still present in the system.
  263. # Something better needs to happen inside
  264. # TrialRunner._runWithoutDecoration to remove the need for this cludge.
  265. r._log = log.LogPublisher()
  266. return r
  267. def test_runner_can_get_reporter(self):
  268. self.parseOptions([])
  269. result = self.config['reporter']
  270. runner = self.getRunner()
  271. self.assertEqual(result, runner._makeResult().__class__)
  272. def test_runner_get_result(self):
  273. self.parseOptions([])
  274. runner = self.getRunner()
  275. result = runner._makeResult()
  276. self.assertEqual(result.__class__, self.config['reporter'])
  277. def test_uncleanWarningsOffByDefault(self):
  278. """
  279. By default Trial sets the 'uncleanWarnings' option on the runner to
  280. False. This means that dirty reactor errors will be reported as
  281. errors. See L{test_reporter.DirtyReactorTests}.
  282. """
  283. self.parseOptions([])
  284. runner = self.getRunner()
  285. self.assertNotIsInstance(runner._makeResult(),
  286. reporter.UncleanWarningsReporterWrapper)
  287. def test_getsUncleanWarnings(self):
  288. """
  289. Specifying '--unclean-warnings' on the trial command line will cause
  290. reporters to be wrapped in a device which converts unclean errors to
  291. warnings. See L{test_reporter.DirtyReactorTests} for implications.
  292. """
  293. self.parseOptions(['--unclean-warnings'])
  294. runner = self.getRunner()
  295. self.assertIsInstance(runner._makeResult(),
  296. reporter.UncleanWarningsReporterWrapper)
  297. def test_runner_working_directory(self):
  298. self.parseOptions(['--temp-directory', 'some_path'])
  299. runner = self.getRunner()
  300. self.assertEqual(runner.workingDirectory, 'some_path')
  301. def test_concurrentImplicitWorkingDirectory(self):
  302. """
  303. If no working directory is explicitly specified and the default
  304. working directory is in use by another runner, L{TrialRunner.run}
  305. selects a different default working directory to use.
  306. """
  307. self.parseOptions([])
  308. # Make sure we end up with the same working directory after this test
  309. # as we had before it.
  310. self.addCleanup(os.chdir, os.getcwd())
  311. # Make a new directory and change into it. This isolates us from state
  312. # that other tests might have dumped into this process's temp
  313. # directory.
  314. runDirectory = FilePath(self.mktemp())
  315. runDirectory.makedirs()
  316. os.chdir(runDirectory.path)
  317. firstRunner = self.getRunner()
  318. secondRunner = self.getRunner()
  319. where = {}
  320. class ConcurrentCase(unittest.SynchronousTestCase):
  321. def test_first(self):
  322. """
  323. Start a second test run which will have a default working
  324. directory which is the same as the working directory of the
  325. test run already in progress.
  326. """
  327. # Change the working directory to the value it had before this
  328. # test suite was started.
  329. where['concurrent'] = subsequentDirectory = os.getcwd()
  330. os.chdir(runDirectory.path)
  331. self.addCleanup(os.chdir, subsequentDirectory)
  332. secondRunner.run(ConcurrentCase('test_second'))
  333. def test_second(self):
  334. """
  335. Record the working directory for later analysis.
  336. """
  337. where['record'] = os.getcwd()
  338. result = firstRunner.run(ConcurrentCase('test_first'))
  339. bad = result.errors + result.failures
  340. if bad:
  341. self.fail(bad[0][1])
  342. self.assertEqual(
  343. where, {
  344. 'concurrent': runDirectory.child('_trial_temp').path,
  345. 'record': runDirectory.child('_trial_temp-1').path})
  346. def test_concurrentExplicitWorkingDirectory(self):
  347. """
  348. If a working directory which is already in use is explicitly specified,
  349. L{TrialRunner.run} raises L{_WorkingDirectoryBusy}.
  350. """
  351. self.parseOptions(['--temp-directory', os.path.abspath(self.mktemp())])
  352. initialDirectory = os.getcwd()
  353. self.addCleanup(os.chdir, initialDirectory)
  354. firstRunner = self.getRunner()
  355. secondRunner = self.getRunner()
  356. class ConcurrentCase(unittest.SynchronousTestCase):
  357. def test_concurrent(self):
  358. """
  359. Try to start another runner in the same working directory and
  360. assert that it raises L{_WorkingDirectoryBusy}.
  361. """
  362. self.assertRaises(
  363. util._WorkingDirectoryBusy,
  364. secondRunner.run, ConcurrentCase('test_failure'))
  365. def test_failure(self):
  366. """
  367. Should not be called, always fails.
  368. """
  369. self.fail("test_failure should never be called.")
  370. result = firstRunner.run(ConcurrentCase('test_concurrent'))
  371. bad = result.errors + result.failures
  372. if bad:
  373. self.fail(bad[0][1])
  374. def test_runner_normal(self):
  375. self.parseOptions(['--temp-directory', self.mktemp(),
  376. '--reporter', 'capturing',
  377. 'twisted.trial.test.sample'])
  378. my_runner = self.getRunner()
  379. loader = runner.TestLoader()
  380. suite = loader.loadByName('twisted.trial.test.sample', True)
  381. result = my_runner.run(suite)
  382. self.assertEqual(self.standardReport, result._calls)
  383. def runSampleSuite(self, my_runner):
  384. loader = runner.TestLoader()
  385. suite = loader.loadByName('twisted.trial.test.sample', True)
  386. return my_runner.run(suite)
  387. def test_runnerDebug(self):
  388. """
  389. Trial uses its debugger if the `--debug` option is passed.
  390. """
  391. self.parseOptions(['--reporter', 'capturing',
  392. '--debug', 'twisted.trial.test.sample'])
  393. my_runner = self.getRunner()
  394. debugger = my_runner.debugger = CapturingDebugger()
  395. result = self.runSampleSuite(my_runner)
  396. self.assertEqual(self.standardReport, result._calls)
  397. self.assertEqual(['runcall'], debugger._calls)
  398. def test_runnerDebuggerDefaultsToPdb(self):
  399. """
  400. Trial uses pdb if no debugger is specified by `--debugger`
  401. """
  402. self.parseOptions(['--debug', 'twisted.trial.test.sample'])
  403. pdbrcFile = FilePath("pdbrc")
  404. pdbrcFile.touch()
  405. self.runcall_called = False
  406. def runcall(pdb, suite, result):
  407. self.runcall_called = True
  408. self.patch(pdb.Pdb, "runcall", runcall)
  409. self.runSampleSuite(self.getRunner())
  410. self.assertTrue(self.runcall_called)
  411. def test_runnerDebuggerWithExplicitlyPassedPdb(self):
  412. """
  413. Trial uses pdb if pdb is passed explicitly to the `--debugger` arg.
  414. """
  415. self.parseOptions([
  416. '--reporter', 'capturing',
  417. '--debugger', 'pdb',
  418. '--debug', 'twisted.trial.test.sample',
  419. ])
  420. self.runcall_called = False
  421. def runcall(pdb, suite, result):
  422. self.runcall_called = True
  423. self.patch(pdb.Pdb, "runcall", runcall)
  424. self.runSampleSuite(self.getRunner())
  425. self.assertTrue(self.runcall_called)
  426. cdebugger = CapturingDebugger()
  427. def test_runnerDebugger(self):
  428. """
  429. Trial uses specified debugger if the debugger is available.
  430. """
  431. self.parseOptions([
  432. '--reporter', 'capturing',
  433. '--debugger',
  434. 'twisted.trial.test.test_runner.RunnerTests.cdebugger',
  435. '--debug',
  436. 'twisted.trial.test.sample',
  437. ])
  438. my_runner = self.getRunner()
  439. result = self.runSampleSuite(my_runner)
  440. self.assertEqual(self.standardReport, result._calls)
  441. self.assertEqual(['runcall'], my_runner.debugger._calls)
  442. def test_exitfirst(self):
  443. """
  444. If trial was passed the C{--exitfirst} option, the constructed test
  445. result object is wrapped with L{reporter._ExitWrapper}.
  446. """
  447. self.parseOptions(["--exitfirst"])
  448. runner = self.getRunner()
  449. result = runner._makeResult()
  450. self.assertIsInstance(result, reporter._ExitWrapper)
  451. class TrialSuiteTests(unittest.SynchronousTestCase):
  452. def test_imports(self):
  453. # FIXME, HTF do you test the reactor can be cleaned up ?!!!
  454. namedAny('twisted.trial.runner.TrialSuite')
  455. class UntilFailureTests(unittest.SynchronousTestCase):
  456. class FailAfter(pyunit.TestCase):
  457. """
  458. A test case that fails when run 3 times in a row.
  459. """
  460. count = []
  461. def test_foo(self):
  462. self.count.append(None)
  463. if len(self.count) == 3:
  464. self.fail('Count reached 3')
  465. def setUp(self):
  466. UntilFailureTests.FailAfter.count = []
  467. self.test = UntilFailureTests.FailAfter('test_foo')
  468. self.stream = NativeStringIO()
  469. self.runner = runner.TrialRunner(reporter.Reporter, stream=self.stream)
  470. def test_runUntilFailure(self):
  471. """
  472. Test that the runUntilFailure method of the runner actually fail after
  473. a few runs.
  474. """
  475. result = self.runner.runUntilFailure(self.test)
  476. self.assertEqual(result.testsRun, 1)
  477. self.assertFalse(result.wasSuccessful())
  478. self.assertEqual(self._getFailures(result), 1)
  479. def _getFailures(self, result):
  480. """
  481. Get the number of failures that were reported to a result.
  482. """
  483. return len(result.failures)
  484. def test_runUntilFailureDecorate(self):
  485. """
  486. C{runUntilFailure} doesn't decorate the tests uselessly: it does it one
  487. time when run starts, but not at each turn.
  488. """
  489. decorated = []
  490. def decorate(test, interface):
  491. decorated.append((test, interface))
  492. return test
  493. self.patch(unittest, "decorate", decorate)
  494. result = self.runner.runUntilFailure(self.test)
  495. self.assertEqual(result.testsRun, 1)
  496. self.assertEqual(len(decorated), 1)
  497. self.assertEqual(decorated, [(self.test, ITestCase)])
  498. def test_runUntilFailureForceGCDecorate(self):
  499. """
  500. C{runUntilFailure} applies the force-gc decoration after the standard
  501. L{ITestCase} decoration, but only one time.
  502. """
  503. decorated = []
  504. def decorate(test, interface):
  505. decorated.append((test, interface))
  506. return test
  507. self.patch(unittest, "decorate", decorate)
  508. self.runner._forceGarbageCollection = True
  509. result = self.runner.runUntilFailure(self.test)
  510. self.assertEqual(result.testsRun, 1)
  511. self.assertEqual(len(decorated), 2)
  512. self.assertEqual(decorated,
  513. [(self.test, ITestCase),
  514. (self.test, _ForceGarbageCollectionDecorator)])
  515. class UncleanUntilFailureTests(UntilFailureTests):
  516. """
  517. Test that the run-until-failure feature works correctly with the unclean
  518. error suppressor.
  519. """
  520. def setUp(self):
  521. UntilFailureTests.setUp(self)
  522. self.runner = runner.TrialRunner(reporter.Reporter, stream=self.stream,
  523. uncleanWarnings=True)
  524. def _getFailures(self, result):
  525. """
  526. Get the number of failures that were reported to a result that
  527. is wrapped in an UncleanFailureWrapper.
  528. """
  529. return len(result._originalReporter.failures)
  530. class BreakingSuite(runner.TestSuite):
  531. """
  532. A L{TestSuite} that logs an error when it is run.
  533. """
  534. def run(self, result):
  535. try:
  536. raise RuntimeError("error that occurs outside of a test")
  537. except RuntimeError:
  538. log.err(failure.Failure())
  539. class LoggedErrorsTests(unittest.SynchronousTestCase):
  540. """
  541. It is possible for an error generated by a test to be logged I{outside} of
  542. any test. The log observers constructed by L{TestCase} won't catch these
  543. errors. Here we try to generate such errors and ensure they are reported to
  544. a L{TestResult} object.
  545. """
  546. def tearDown(self):
  547. self.flushLoggedErrors(RuntimeError)
  548. def test_construct(self):
  549. """
  550. Check that we can construct a L{runner.LoggedSuite} and that it
  551. starts empty.
  552. """
  553. suite = runner.LoggedSuite()
  554. self.assertEqual(suite.countTestCases(), 0)
  555. def test_capturesError(self):
  556. """
  557. Chek that a L{LoggedSuite} reports any logged errors to its result.
  558. """
  559. result = reporter.TestResult()
  560. suite = runner.LoggedSuite([BreakingSuite()])
  561. suite.run(result)
  562. self.assertEqual(len(result.errors), 1)
  563. self.assertEqual(result.errors[0][0].id(), runner.NOT_IN_TEST)
  564. self.assertTrue(result.errors[0][1].check(RuntimeError))
  565. class TestHolderTests(unittest.SynchronousTestCase):
  566. def setUp(self):
  567. self.description = "description"
  568. self.holder = runner.TestHolder(self.description)
  569. def test_holder(self):
  570. """
  571. Check that L{runner.TestHolder} takes a description as a parameter
  572. and that this description is returned by the C{id} and
  573. C{shortDescription} methods.
  574. """
  575. self.assertEqual(self.holder.id(), self.description)
  576. self.assertEqual(self.holder.shortDescription(), self.description)
  577. def test_holderImplementsITestCase(self):
  578. """
  579. L{runner.TestHolder} implements L{ITestCase}.
  580. """
  581. self.assertIdentical(self.holder, ITestCase(self.holder))
  582. self.assertTrue(
  583. verifyObject(ITestCase, self.holder),
  584. "%r claims to provide %r but does not do so correctly."
  585. % (self.holder, ITestCase))
  586. def test_runsWithStandardResult(self):
  587. """
  588. A L{runner.TestHolder} can run against the standard Python
  589. C{TestResult}.
  590. """
  591. result = pyunit.TestResult()
  592. self.holder.run(result)
  593. self.assertTrue(result.wasSuccessful())
  594. self.assertEqual(1, result.testsRun)
  595. class ErrorHolderTestsMixin(object):
  596. """
  597. This mixin defines test methods which can be applied to a
  598. L{runner.ErrorHolder} constructed with either a L{Failure} or a
  599. C{exc_info}-style tuple.
  600. Subclass this and implement C{setUp} to create C{self.holder} referring to a
  601. L{runner.ErrorHolder} instance and C{self.error} referring to a L{Failure}
  602. which the holder holds.
  603. """
  604. exceptionForTests = ZeroDivisionError('integer division or modulo by zero')
  605. class TestResultStub(object):
  606. """
  607. Stub for L{TestResult}.
  608. """
  609. def __init__(self):
  610. self.errors = []
  611. def startTest(self, test):
  612. pass
  613. def stopTest(self, test):
  614. pass
  615. def addError(self, test, error):
  616. self.errors.append((test, error))
  617. def test_runsWithStandardResult(self):
  618. """
  619. A L{runner.ErrorHolder} can run against the standard Python
  620. C{TestResult}.
  621. """
  622. result = pyunit.TestResult()
  623. self.holder.run(result)
  624. self.assertFalse(result.wasSuccessful())
  625. self.assertEqual(1, result.testsRun)
  626. def test_run(self):
  627. """
  628. L{runner.ErrorHolder} adds an error to the result when run.
  629. """
  630. self.holder.run(self.result)
  631. self.assertEqual(
  632. self.result.errors,
  633. [(self.holder, (self.error.type, self.error.value, self.error.tb))])
  634. def test_call(self):
  635. """
  636. L{runner.ErrorHolder} adds an error to the result when called.
  637. """
  638. self.holder(self.result)
  639. self.assertEqual(
  640. self.result.errors,
  641. [(self.holder, (self.error.type, self.error.value, self.error.tb))])
  642. def test_countTestCases(self):
  643. """
  644. L{runner.ErrorHolder.countTestCases} always returns 0.
  645. """
  646. self.assertEqual(self.holder.countTestCases(), 0)
  647. def test_repr(self):
  648. """
  649. L{runner.ErrorHolder.__repr__} returns a string describing the error it
  650. holds.
  651. """
  652. self.assertEqual(repr(self.holder),
  653. "<ErrorHolder description='description' "
  654. "error=ZeroDivisionError('integer division or modulo by zero',)>")
  655. class FailureHoldingErrorHolderTests(ErrorHolderTestsMixin, TestHolderTests):
  656. """
  657. Tests for L{runner.ErrorHolder} behaving similarly to L{runner.TestHolder}
  658. when constructed with a L{Failure} representing its error.
  659. """
  660. def setUp(self):
  661. self.description = "description"
  662. # make a real Failure so we can construct ErrorHolder()
  663. try:
  664. raise self.exceptionForTests
  665. except ZeroDivisionError:
  666. self.error = failure.Failure()
  667. self.holder = runner.ErrorHolder(self.description, self.error)
  668. self.result = self.TestResultStub()
  669. class ExcInfoHoldingErrorHolderTests(ErrorHolderTestsMixin, TestHolderTests):
  670. """
  671. Tests for L{runner.ErrorHolder} behaving similarly to L{runner.TestHolder}
  672. when constructed with a C{exc_info}-style tuple representing its error.
  673. """
  674. def setUp(self):
  675. self.description = "description"
  676. # make a real Failure so we can construct ErrorHolder()
  677. try:
  678. raise self.exceptionForTests
  679. except ZeroDivisionError:
  680. exceptionInfo = sys.exc_info()
  681. self.error = failure.Failure()
  682. self.holder = runner.ErrorHolder(self.description, exceptionInfo)
  683. self.result = self.TestResultStub()
  684. class MalformedMethodTests(unittest.SynchronousTestCase):
  685. """
  686. Test that trial manages when test methods don't have correct signatures.
  687. """
  688. class ContainMalformed(pyunit.TestCase):
  689. """
  690. This TestCase holds malformed test methods that trial should handle.
  691. """
  692. def test_foo(self, blah):
  693. pass
  694. def test_bar():
  695. pass
  696. test_spam = defer.inlineCallbacks(test_bar)
  697. def _test(self, method):
  698. """
  699. Wrapper for one of the test method of L{ContainMalformed}.
  700. """
  701. stream = NativeStringIO()
  702. trialRunner = runner.TrialRunner(reporter.Reporter, stream=stream)
  703. test = MalformedMethodTests.ContainMalformed(method)
  704. result = trialRunner.run(test)
  705. self.assertEqual(result.testsRun, 1)
  706. self.assertFalse(result.wasSuccessful())
  707. self.assertEqual(len(result.errors), 1)
  708. def test_extraArg(self):
  709. """
  710. Test when the method has extra (useless) arguments.
  711. """
  712. self._test('test_foo')
  713. def test_noArg(self):
  714. """
  715. Test when the method doesn't have even self as argument.
  716. """
  717. self._test('test_bar')
  718. def test_decorated(self):
  719. """
  720. Test a decorated method also fails.
  721. """
  722. self._test('test_spam')
  723. class DestructiveTestSuiteTests(unittest.SynchronousTestCase):
  724. """
  725. Test for L{runner.DestructiveTestSuite}.
  726. """
  727. def test_basic(self):
  728. """
  729. Thes destructive test suite should run the tests normally.
  730. """
  731. called = []
  732. class MockTest(pyunit.TestCase):
  733. def test_foo(test):
  734. called.append(True)
  735. test = MockTest('test_foo')
  736. result = reporter.TestResult()
  737. suite = runner.DestructiveTestSuite([test])
  738. self.assertEqual(called, [])
  739. suite.run(result)
  740. self.assertEqual(called, [True])
  741. self.assertEqual(suite.countTestCases(), 0)
  742. def test_shouldStop(self):
  743. """
  744. Test the C{shouldStop} management: raising a C{KeyboardInterrupt} must
  745. interrupt the suite.
  746. """
  747. called = []
  748. class MockTest(unittest.TestCase):
  749. def test_foo1(test):
  750. called.append(1)
  751. def test_foo2(test):
  752. raise KeyboardInterrupt()
  753. def test_foo3(test):
  754. called.append(2)
  755. result = reporter.TestResult()
  756. loader = runner.TestLoader()
  757. loader.suiteFactory = runner.DestructiveTestSuite
  758. suite = loader.loadClass(MockTest)
  759. self.assertEqual(called, [])
  760. suite.run(result)
  761. self.assertEqual(called, [1])
  762. # The last test shouldn't have been run
  763. self.assertEqual(suite.countTestCases(), 1)
  764. def test_cleanup(self):
  765. """
  766. Checks that the test suite cleanups its tests during the run, so that
  767. it ends empty.
  768. """
  769. class MockTest(pyunit.TestCase):
  770. def test_foo(test):
  771. pass
  772. test = MockTest('test_foo')
  773. result = reporter.TestResult()
  774. suite = runner.DestructiveTestSuite([test])
  775. self.assertEqual(suite.countTestCases(), 1)
  776. suite.run(result)
  777. self.assertEqual(suite.countTestCases(), 0)
  778. class RunnerDeprecationTests(unittest.SynchronousTestCase):
  779. class FakeReporter(reporter.Reporter):
  780. """
  781. Fake reporter that does *not* implement done() but *does* implement
  782. printErrors, separator, printSummary, stream, write and writeln
  783. without deprecations.
  784. """
  785. done = None
  786. separator = None
  787. stream = None
  788. def printErrors(self, *args):
  789. pass
  790. def printSummary(self, *args):
  791. pass
  792. def write(self, *args):
  793. pass
  794. def writeln(self, *args):
  795. pass
  796. def test_reporterDeprecations(self):
  797. """
  798. The runner emits a warning if it is using a result that doesn't
  799. implement 'done'.
  800. """
  801. trialRunner = runner.TrialRunner(None)
  802. result = self.FakeReporter()
  803. trialRunner._makeResult = lambda: result
  804. def f():
  805. # We have to use a pyunit test, otherwise we'll get deprecation
  806. # warnings about using iterate() in a test.
  807. trialRunner.run(pyunit.TestCase('id'))
  808. f()
  809. warnings = self.flushWarnings([self.test_reporterDeprecations])
  810. self.assertEqual(warnings[0]['category'], DeprecationWarning)
  811. self.assertEqual(warnings[0]['message'],
  812. "%s should implement done() but doesn't. Falling back to "
  813. "printErrors() and friends." % reflect.qual(result.__class__))
  814. self.assertTrue(__file__.startswith(warnings[0]['filename']))
  815. self.assertEqual(len(warnings), 1)
  816. class QualifiedNameWalkerTests(unittest.SynchronousTestCase):
  817. """
  818. Tests for L{twisted.trial.runner._qualNameWalker}.
  819. """
  820. def test_walksDownPath(self):
  821. """
  822. C{_qualNameWalker} is a generator that, when given a Python qualified
  823. name, yields that name, and then the parent of that name, and so forth,
  824. along with a list of the tried components, in a 2-tuple.
  825. """
  826. walkerResults = list(runner._qualNameWalker("walker.texas.ranger"))
  827. self.assertEqual(walkerResults,
  828. [("walker.texas.ranger", []),
  829. ("walker.texas", ["ranger"]),
  830. ("walker", ["texas", "ranger"])])
  831. class TrialMainDoesNothingTests(unittest.SynchronousTestCase):
  832. """
  833. Importing L{twisted.trial.__main__} will not run the script
  834. unless it is actually C{__main__}.
  835. """
  836. def test_importDoesNothing(self):
  837. """
  838. If we import L{twisted.trial.__main__}, it should do nothing.
  839. """
  840. # We shouldn't suddenly drop into a script when we import this!
  841. __import__('twisted.trial.__main__')