| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2001-2007 Twisted Matrix Laboratories. | |
| 2 # See LICENSE for details. | |
| 3 # | |
| 4 # Maintainer: Jonathan Lange <jml@twistedmatrix.com> | |
| 5 | |
| 6 | |
| 7 import errno, sys, os, re, StringIO | |
| 8 from twisted.internet.utils import suppressWarnings | |
| 9 from twisted.python.failure import Failure | |
| 10 from twisted.trial import itrial, unittest, runner, reporter, util | |
| 11 from twisted.trial.reporter import UncleanWarningsReporterWrapper | |
| 12 from twisted.trial.test import erroneous | |
| 13 from twisted.trial.unittest import makeTodo, SkipTest, Todo | |
| 14 | |
| 15 | |
| 16 class BrokenStream(object): | |
| 17 """ | |
| 18 Stream-ish object that raises a signal interrupt error. We use this to make | |
| 19 sure that Trial still manages to write what it needs to write. | |
| 20 """ | |
| 21 written = False | |
| 22 flushed = False | |
| 23 | |
| 24 def __init__(self, fObj): | |
| 25 self.fObj = fObj | |
| 26 | |
| 27 def write(self, s): | |
| 28 if self.written: | |
| 29 return self.fObj.write(s) | |
| 30 self.written = True | |
| 31 raise IOError(errno.EINTR, "Interrupted write") | |
| 32 | |
| 33 def flush(self): | |
| 34 if self.flushed: | |
| 35 return self.fObj.flush() | |
| 36 self.flushed = True | |
| 37 raise IOError(errno.EINTR, "Interrupted flush") | |
| 38 | |
| 39 | |
| 40 class StringTest(unittest.TestCase): | |
| 41 def stringComparison(self, expect, output): | |
| 42 output = filter(None, output) | |
| 43 self.failUnless(len(expect) <= len(output), | |
| 44 "Must have more observed than expected" | |
| 45 "lines %d < %d" % (len(output), len(expect))) | |
| 46 REGEX_PATTERN_TYPE = type(re.compile('')) | |
| 47 for line_number, (exp, out) in enumerate(zip(expect, output)): | |
| 48 if exp is None: | |
| 49 continue | |
| 50 elif isinstance(exp, str): | |
| 51 self.assertSubstring(exp, out, "Line %d: %r not in %r" | |
| 52 % (line_number, exp, out)) | |
| 53 elif isinstance(exp, REGEX_PATTERN_TYPE): | |
| 54 self.failUnless(exp.match(out), | |
| 55 "Line %d: %r did not match string %r" | |
| 56 % (line_number, exp.pattern, out)) | |
| 57 else: | |
| 58 raise TypeError("don't know what to do with object %r" | |
| 59 % (exp,)) | |
| 60 | |
| 61 | |
| 62 class TestTestResult(unittest.TestCase): | |
| 63 def setUp(self): | |
| 64 self.result = reporter.TestResult() | |
| 65 | |
| 66 def test_pyunitAddError(self): | |
| 67 # pyunit passes an exc_info tuple directly to addError | |
| 68 try: | |
| 69 raise RuntimeError('foo') | |
| 70 except RuntimeError, excValue: | |
| 71 self.result.addError(self, sys.exc_info()) | |
| 72 failure = self.result.errors[0][1] | |
| 73 self.assertEqual(excValue, failure.value) | |
| 74 self.assertEqual(RuntimeError, failure.type) | |
| 75 | |
| 76 def test_pyunitAddFailure(self): | |
| 77 # pyunit passes an exc_info tuple directly to addFailure | |
| 78 try: | |
| 79 raise self.failureException('foo') | |
| 80 except self.failureException, excValue: | |
| 81 self.result.addFailure(self, sys.exc_info()) | |
| 82 failure = self.result.failures[0][1] | |
| 83 self.assertEqual(excValue, failure.value) | |
| 84 self.assertEqual(self.failureException, failure.type) | |
| 85 | |
| 86 | |
| 87 class TestReporterRealtime(TestTestResult): | |
| 88 def setUp(self): | |
| 89 output = StringIO.StringIO() | |
| 90 self.result = reporter.Reporter(output, realtime=True) | |
| 91 | |
| 92 | |
| 93 class TestErrorReporting(StringTest): | |
| 94 doubleSeparator = re.compile(r'^=+$') | |
| 95 | |
| 96 def setUp(self): | |
| 97 self.loader = runner.TestLoader() | |
| 98 self.output = StringIO.StringIO() | |
| 99 self.result = reporter.Reporter(self.output) | |
| 100 | |
| 101 def getOutput(self, suite): | |
| 102 result = self.getResult(suite) | |
| 103 result.done() | |
| 104 return self.output.getvalue() | |
| 105 | |
| 106 def getResult(self, suite): | |
| 107 suite.run(self.result) | |
| 108 return self.result | |
| 109 | |
| 110 def testFormatErroredMethod(self): | |
| 111 suite = self.loader.loadClass(erroneous.TestFailureInSetUp) | |
| 112 output = self.getOutput(suite).splitlines() | |
| 113 match = [self.doubleSeparator, | |
| 114 ('[ERROR]: twisted.trial.test.erroneous.' | |
| 115 'TestFailureInSetUp.test_noop'), | |
| 116 'Traceback (most recent call last):', | |
| 117 re.compile(r'^\s+File .*erroneous\.py., line \d+, in setUp$'), | |
| 118 re.compile(r'^\s+raise FoolishError, ' | |
| 119 r'.I am a broken setUp method.$'), | |
| 120 ('twisted.trial.test.erroneous.FoolishError: ' | |
| 121 'I am a broken setUp method')] | |
| 122 self.stringComparison(match, output) | |
| 123 | |
| 124 def testFormatFailedMethod(self): | |
| 125 suite = self.loader.loadMethod(erroneous.TestRegularFail.test_fail) | |
| 126 output = self.getOutput(suite).splitlines() | |
| 127 match = [ | |
| 128 self.doubleSeparator, | |
| 129 '[FAIL]: ' | |
| 130 'twisted.trial.test.erroneous.TestRegularFail.test_fail', | |
| 131 'Traceback (most recent call last):', | |
| 132 re.compile(r'^\s+File .*erroneous\.py., line \d+, in test_fail$'), | |
| 133 re.compile(r'^\s+self\.fail\("I fail"\)$'), | |
| 134 'twisted.trial.unittest.FailTest: I fail' | |
| 135 ] | |
| 136 self.stringComparison(match, output) | |
| 137 | |
| 138 def testDoctestError(self): | |
| 139 from twisted.trial.test import erroneous | |
| 140 suite = unittest.decorate( | |
| 141 self.loader.loadDoctests(erroneous), itrial.ITestCase) | |
| 142 output = self.getOutput(suite) | |
| 143 path = 'twisted.trial.test.erroneous.unexpectedException' | |
| 144 for substring in ['1/0', 'ZeroDivisionError', | |
| 145 'Exception raised:', path]: | |
| 146 self.assertSubstring(substring, output) | |
| 147 self.failUnless(re.search('Fail(ed|ure in) example:', output), | |
| 148 "Couldn't match 'Failure in example: ' " | |
| 149 "or 'Failed example: '") | |
| 150 expect = [self.doubleSeparator, | |
| 151 re.compile(r'\[(ERROR|FAIL)\]: .*' + re.escape(path))] | |
| 152 self.stringComparison(expect, output.splitlines()) | |
| 153 | |
| 154 def testHiddenException(self): | |
| 155 """ | |
| 156 Check that errors in C{DelayedCall}s get reported, even if the | |
| 157 test already has a failure. | |
| 158 | |
| 159 Only really necessary for testing the deprecated style of tests that | |
| 160 use iterate() directly. See | |
| 161 L{erroneous.DelayedCall.testHiddenException} for more details. | |
| 162 """ | |
| 163 test = erroneous.DelayedCall('testHiddenException') | |
| 164 output = self.getOutput(test).splitlines() | |
| 165 match = [ | |
| 166 self.doubleSeparator, | |
| 167 '[FAIL]: ' | |
| 168 'twisted.trial.test.erroneous.DelayedCall.testHiddenException', | |
| 169 'Traceback (most recent call last):', | |
| 170 re.compile(r'^\s+File .*erroneous\.py., line \d+, in ' | |
| 171 'testHiddenException$'), | |
| 172 re.compile(r'^\s+self\.fail\("Deliberate failure to mask the ' | |
| 173 'hidden exception"\)$'), | |
| 174 'twisted.trial.unittest.FailTest: ' | |
| 175 'Deliberate failure to mask the hidden exception', | |
| 176 self.doubleSeparator, | |
| 177 '[ERROR]: ' | |
| 178 'twisted.trial.test.erroneous.DelayedCall.testHiddenException', | |
| 179 'Traceback (most recent call last):', | |
| 180 re.compile(r'^\s+File .* in runUntilCurrent'), | |
| 181 re.compile(r'^\s+.*'), | |
| 182 re.compile('^\s+File .*erroneous\.py", line \d+, in go'), | |
| 183 re.compile('^\s+raise RuntimeError\(self.hiddenExceptionMsg\)'), | |
| 184 'exceptions.RuntimeError: something blew up', | |
| 185 ] | |
| 186 | |
| 187 self.stringComparison(match, output) | |
| 188 | |
| 189 | |
| 190 | |
| 191 class TestUncleanWarningWrapperErrorReporting(TestErrorReporting): | |
| 192 """ | |
| 193 Tests that the L{UncleanWarningsReporterWrapper} can sufficiently proxy | |
| 194 IReporter failure and error reporting methods to a L{reporter.Reporter}. | |
| 195 """ | |
| 196 def setUp(self): | |
| 197 self.loader = runner.TestLoader() | |
| 198 self.output = StringIO.StringIO() | |
| 199 self.result = UncleanWarningsReporterWrapper( | |
| 200 reporter.Reporter(self.output)) | |
| 201 | |
| 202 | |
| 203 | |
| 204 class TracebackHandling(unittest.TestCase): | |
| 205 def getErrorFrames(self, test): | |
| 206 stream = StringIO.StringIO() | |
| 207 result = reporter.Reporter(stream) | |
| 208 test.run(result) | |
| 209 bads = result.failures + result.errors | |
| 210 assert len(bads) == 1 | |
| 211 assert bads[0][0] == test | |
| 212 return result._trimFrames(bads[0][1].frames) | |
| 213 | |
| 214 def checkFrames(self, observedFrames, expectedFrames): | |
| 215 for observed, expected in zip(observedFrames, expectedFrames): | |
| 216 self.assertEqual(observed[0], expected[0]) | |
| 217 observedSegs = os.path.splitext(observed[1])[0].split(os.sep) | |
| 218 expectedSegs = expected[1].split('/') | |
| 219 self.assertEqual(observedSegs[-len(expectedSegs):], | |
| 220 expectedSegs) | |
| 221 self.assertEqual(len(observedFrames), len(expectedFrames)) | |
| 222 | |
| 223 def test_basic(self): | |
| 224 test = erroneous.TestRegularFail('test_fail') | |
| 225 frames = self.getErrorFrames(test) | |
| 226 self.checkFrames(frames, | |
| 227 [('test_fail', 'twisted/trial/test/erroneous')]) | |
| 228 | |
| 229 def test_subroutine(self): | |
| 230 test = erroneous.TestRegularFail('test_subfail') | |
| 231 frames = self.getErrorFrames(test) | |
| 232 self.checkFrames(frames, | |
| 233 [('test_subfail', 'twisted/trial/test/erroneous'), | |
| 234 ('subroutine', 'twisted/trial/test/erroneous')]) | |
| 235 | |
| 236 def test_deferred(self): | |
| 237 test = erroneous.TestFailureInDeferredChain('test_fail') | |
| 238 frames = self.getErrorFrames(test) | |
| 239 self.checkFrames(frames, | |
| 240 [('_later', 'twisted/trial/test/erroneous')]) | |
| 241 | |
| 242 def test_noFrames(self): | |
| 243 result = reporter.Reporter(None) | |
| 244 self.assertEqual([], result._trimFrames([])) | |
| 245 | |
| 246 def test_oneFrame(self): | |
| 247 result = reporter.Reporter(None) | |
| 248 self.assertEqual(['fake frame'], result._trimFrames(['fake frame'])) | |
| 249 | |
| 250 | |
| 251 class FormatFailures(StringTest): | |
| 252 def setUp(self): | |
| 253 try: | |
| 254 raise RuntimeError('foo') | |
| 255 except RuntimeError: | |
| 256 self.f = Failure() | |
| 257 self.f.frames = [ | |
| 258 ['foo', 'foo/bar.py', 5, [('x', 5)], [('y', 'orange')]], | |
| 259 ['qux', 'foo/bar.py', 10, [('a', 'two')], [('b', 'MCMXCIX')]] | |
| 260 ] | |
| 261 self.stream = StringIO.StringIO() | |
| 262 self.result = reporter.Reporter(self.stream) | |
| 263 | |
| 264 def test_formatDefault(self): | |
| 265 tb = self.result._formatFailureTraceback(self.f) | |
| 266 self.stringComparison([ | |
| 267 'Traceback (most recent call last):', | |
| 268 ' File "foo/bar.py", line 5, in foo', | |
| 269 re.compile(r'^\s*$'), | |
| 270 ' File "foo/bar.py", line 10, in qux', | |
| 271 re.compile(r'^\s*$'), | |
| 272 'RuntimeError: foo'], tb.splitlines()) | |
| 273 | |
| 274 def test_formatString(self): | |
| 275 tb = ''' | |
| 276 File "twisted/trial/unittest.py", line 256, in failUnlessSubstring | |
| 277 return self.failUnlessIn(substring, astring, msg) | |
| 278 exceptions.TypeError: iterable argument required | |
| 279 | |
| 280 ''' | |
| 281 expected = ''' | |
| 282 File "twisted/trial/unittest.py", line 256, in failUnlessSubstring | |
| 283 return self.failUnlessIn(substring, astring, msg) | |
| 284 exceptions.TypeError: iterable argument required | |
| 285 ''' | |
| 286 formatted = self.result._formatFailureTraceback(tb) | |
| 287 self.assertEqual(expected, formatted) | |
| 288 | |
| 289 def test_mutation(self): | |
| 290 frames = self.f.frames[:] | |
| 291 tb = self.result._formatFailureTraceback(self.f) | |
| 292 self.assertEqual(self.f.frames, frames) | |
| 293 | |
| 294 | |
| 295 class PyunitTestNames(unittest.TestCase): | |
| 296 def setUp(self): | |
| 297 from twisted.trial.test import sample | |
| 298 self.stream = StringIO.StringIO() | |
| 299 self.test = sample.PyunitTest('test_foo') | |
| 300 | |
| 301 def test_verboseReporter(self): | |
| 302 result = reporter.VerboseTextReporter(self.stream) | |
| 303 result.startTest(self.test) | |
| 304 output = self.stream.getvalue() | |
| 305 self.failUnlessEqual( | |
| 306 output, 'twisted.trial.test.sample.PyunitTest.test_foo ... ') | |
| 307 | |
| 308 def test_treeReporter(self): | |
| 309 result = reporter.TreeReporter(self.stream) | |
| 310 result.startTest(self.test) | |
| 311 output = self.stream.getvalue() | |
| 312 output = output.splitlines()[-1].strip() | |
| 313 self.failUnlessEqual(output, result.getDescription(self.test) + ' ...') | |
| 314 | |
| 315 def test_getDescription(self): | |
| 316 result = reporter.TreeReporter(self.stream) | |
| 317 output = result.getDescription(self.test) | |
| 318 self.failUnlessEqual(output, 'test_foo') | |
| 319 | |
| 320 | |
| 321 def test_minimalReporter(self): | |
| 322 """ | |
| 323 The summary of L{reporter.MinimalReporter} is a simple list of | |
| 324 numbers, indicating how many tests ran, how many failed etc. | |
| 325 """ | |
| 326 result = reporter.MinimalReporter(self.stream) | |
| 327 self.test.run(result) | |
| 328 result._printSummary() | |
| 329 output = self.stream.getvalue().strip().split(' ') | |
| 330 self.failUnlessEqual(output[1:], ['1', '1', '0', '0', '0']) | |
| 331 | |
| 332 | |
| 333 | |
| 334 class TestDirtyReactor(unittest.TestCase): | |
| 335 """ | |
| 336 The trial script has an option to treat L{DirtyReactorAggregateError}s as | |
| 337 warnings, as a migration tool for test authors. It causes a wrapper to be | |
| 338 placed around reporters that replaces L{DirtyReactorAggregatErrors} with | |
| 339 warnings. | |
| 340 """ | |
| 341 | |
| 342 def setUp(self): | |
| 343 self.dirtyError = Failure( | |
| 344 util.DirtyReactorAggregateError(['foo'], ['bar'])) | |
| 345 self.output = StringIO.StringIO() | |
| 346 self.test = TestDirtyReactor('test_errorByDefault') | |
| 347 | |
| 348 | |
| 349 def test_errorByDefault(self): | |
| 350 """ | |
| 351 C{DirtyReactorAggregateError}s are reported as errors with the default | |
| 352 Reporter. | |
| 353 """ | |
| 354 result = reporter.Reporter(stream=self.output) | |
| 355 result.addError(self.test, self.dirtyError) | |
| 356 self.assertEqual(len(result.errors), 1) | |
| 357 self.assertEqual(result.errors[0][1], self.dirtyError) | |
| 358 | |
| 359 | |
| 360 def test_warningsEnabled(self): | |
| 361 """ | |
| 362 C{DirtyReactorErrors}s are reported as warnings when using the | |
| 363 L{UncleanWarningsReporterWrapper}. | |
| 364 """ | |
| 365 result = UncleanWarningsReporterWrapper( | |
| 366 reporter.Reporter(stream=self.output)) | |
| 367 self.assertWarns(UserWarning, self.dirtyError.getErrorMessage(), | |
| 368 reporter.__file__, | |
| 369 result.addError, self.test, self.dirtyError) | |
| 370 | |
| 371 | |
| 372 def test_warningsMaskErrors(self): | |
| 373 """ | |
| 374 C{DirtyReactorErrors}s are I{not} reported as errors if the | |
| 375 L{UncleanWarningsReporterWrapper} is used. | |
| 376 """ | |
| 377 result = UncleanWarningsReporterWrapper( | |
| 378 reporter.Reporter(stream=self.output)) | |
| 379 self.assertWarns(UserWarning, self.dirtyError.getErrorMessage(), | |
| 380 reporter.__file__, | |
| 381 result.addError, self.test, self.dirtyError) | |
| 382 self.assertEquals(result._originalReporter.errors, []) | |
| 383 | |
| 384 | |
| 385 def test_dealsWithThreeTuples(self): | |
| 386 """ | |
| 387 Some annoying stuff can pass three-tuples to addError instead of | |
| 388 Failures (like PyUnit). The wrapper, of course, handles this case, | |
| 389 since it is a part of L{twisted.trial.itrial.IReporter}! But it does | |
| 390 not convert L{DirtyReactorError} to warnings in this case, because | |
| 391 nobody should be passing those in the form of three-tuples. | |
| 392 """ | |
| 393 result = UncleanWarningsReporterWrapper( | |
| 394 reporter.Reporter(stream=self.output)) | |
| 395 result.addError(self.test, | |
| 396 (self.dirtyError.type, self.dirtyError.value, None)) | |
| 397 self.assertEqual(len(result._originalReporter.errors), 1) | |
| 398 self.assertEquals(result._originalReporter.errors[0][1].type, | |
| 399 self.dirtyError.type) | |
| 400 self.assertEquals(result._originalReporter.errors[0][1].value, | |
| 401 self.dirtyError.value) | |
| 402 | |
| 403 | |
| 404 | |
| 405 class TrialTestNames(unittest.TestCase): | |
| 406 | |
| 407 def setUp(self): | |
| 408 from twisted.trial.test import sample | |
| 409 self.stream = StringIO.StringIO() | |
| 410 self.test = sample.FooTest('test_foo') | |
| 411 | |
| 412 def test_verboseReporter(self): | |
| 413 result = reporter.VerboseTextReporter(self.stream) | |
| 414 result.startTest(self.test) | |
| 415 output = self.stream.getvalue() | |
| 416 self.failUnlessEqual(output, self.test.id() + ' ... ') | |
| 417 | |
| 418 def test_treeReporter(self): | |
| 419 result = reporter.TreeReporter(self.stream) | |
| 420 result.startTest(self.test) | |
| 421 output = self.stream.getvalue() | |
| 422 output = output.splitlines()[-1].strip() | |
| 423 self.failUnlessEqual(output, result.getDescription(self.test) + ' ...') | |
| 424 | |
| 425 def test_treeReporterWithDocstrings(self): | |
| 426 """A docstring""" | |
| 427 result = reporter.TreeReporter(self.stream) | |
| 428 self.assertEqual(result.getDescription(self), | |
| 429 'test_treeReporterWithDocstrings') | |
| 430 | |
| 431 def test_getDescription(self): | |
| 432 result = reporter.TreeReporter(self.stream) | |
| 433 output = result.getDescription(self.test) | |
| 434 self.failUnlessEqual(output, "test_foo") | |
| 435 | |
| 436 | |
| 437 class TestSkip(unittest.TestCase): | |
| 438 """ | |
| 439 Tests for L{reporter.Reporter}'s handling of skips. | |
| 440 """ | |
| 441 def setUp(self): | |
| 442 from twisted.trial.test import sample | |
| 443 self.stream = StringIO.StringIO() | |
| 444 self.result = reporter.Reporter(self.stream) | |
| 445 self.test = sample.FooTest('test_foo') | |
| 446 | |
| 447 def _getSkips(self, result): | |
| 448 """ | |
| 449 Get the number of skips that happened to a reporter. | |
| 450 """ | |
| 451 return len(result.skips) | |
| 452 | |
| 453 def test_accumulation(self): | |
| 454 self.result.addSkip(self.test, 'some reason') | |
| 455 self.assertEqual(self._getSkips(self.result), 1) | |
| 456 | |
| 457 def test_success(self): | |
| 458 self.result.addSkip(self.test, 'some reason') | |
| 459 self.failUnlessEqual(True, self.result.wasSuccessful()) | |
| 460 | |
| 461 | |
| 462 def test_summary(self): | |
| 463 """ | |
| 464 The summary of a successful run with skips indicates that the test | |
| 465 suite passed and includes the number of skips. | |
| 466 """ | |
| 467 self.result.addSkip(self.test, 'some reason') | |
| 468 self.result.done() | |
| 469 output = self.stream.getvalue().splitlines()[-1] | |
| 470 prefix = 'PASSED ' | |
| 471 self.failUnless(output.startswith(prefix)) | |
| 472 self.failUnlessEqual(output[len(prefix):].strip(), '(skips=1)') | |
| 473 | |
| 474 | |
| 475 def test_basicErrors(self): | |
| 476 """ | |
| 477 The output at the end of a test run with skips includes the reasons | |
| 478 for skipping those tests. | |
| 479 """ | |
| 480 self.result.addSkip(self.test, 'some reason') | |
| 481 self.result.done() | |
| 482 output = self.stream.getvalue().splitlines()[4] | |
| 483 self.failUnlessEqual(output.strip(), 'some reason') | |
| 484 | |
| 485 | |
| 486 def test_booleanSkip(self): | |
| 487 """ | |
| 488 Tests can be skipped without specifying a reason by setting the 'skip' | |
| 489 attribute to True. When this happens, the test output includes 'True' | |
| 490 as the reason. | |
| 491 """ | |
| 492 self.result.addSkip(self.test, True) | |
| 493 self.result.done() | |
| 494 output = self.stream.getvalue().splitlines()[4] | |
| 495 self.failUnlessEqual(output, 'True') | |
| 496 | |
| 497 | |
| 498 def test_exceptionSkip(self): | |
| 499 """ | |
| 500 Skips can be raised as errors. When this happens, the error is | |
| 501 included in the summary at the end of the test suite. | |
| 502 """ | |
| 503 try: | |
| 504 1/0 | |
| 505 except Exception, e: | |
| 506 error = e | |
| 507 self.result.addSkip(self.test, error) | |
| 508 self.result.done() | |
| 509 output = '\n'.join(self.stream.getvalue().splitlines()[3:5]).strip() | |
| 510 self.failUnlessEqual(output, str(e)) | |
| 511 | |
| 512 | |
| 513 class UncleanWarningSkipTest(TestSkip): | |
| 514 """ | |
| 515 Tests for skips on a L{reporter.Reporter} wrapped by an | |
| 516 L{UncleanWarningsReporterWrapper}. | |
| 517 """ | |
| 518 def setUp(self): | |
| 519 TestSkip.setUp(self) | |
| 520 self.result = UncleanWarningsReporterWrapper(self.result) | |
| 521 | |
| 522 def _getSkips(self, result): | |
| 523 """ | |
| 524 Get the number of skips that happened to a reporter inside of an | |
| 525 unclean warnings reporter wrapper. | |
| 526 """ | |
| 527 return len(result._originalReporter.skips) | |
| 528 | |
| 529 | |
| 530 | |
| 531 class TodoTest(unittest.TestCase): | |
| 532 """ | |
| 533 Tests for L{reporter.Reporter}'s handling of todos. | |
| 534 """ | |
| 535 | |
| 536 def setUp(self): | |
| 537 from twisted.trial.test import sample | |
| 538 self.stream = StringIO.StringIO() | |
| 539 self.result = reporter.Reporter(self.stream) | |
| 540 self.test = sample.FooTest('test_foo') | |
| 541 | |
| 542 | |
| 543 def _getTodos(self, result): | |
| 544 """ | |
| 545 Get the number of todos that happened to a reporter. | |
| 546 """ | |
| 547 return len(result.expectedFailures) | |
| 548 | |
| 549 | |
| 550 def _getUnexpectedSuccesses(self, result): | |
| 551 """ | |
| 552 Get the number of unexpected successes that happened to a reporter. | |
| 553 """ | |
| 554 return len(result.unexpectedSuccesses) | |
| 555 | |
| 556 | |
| 557 def test_accumulation(self): | |
| 558 """ | |
| 559 L{reporter.Reporter} accumulates the expected failures that it | |
| 560 is notified of. | |
| 561 """ | |
| 562 self.result.addExpectedFailure(self.test, Failure(Exception()), | |
| 563 makeTodo('todo!')) | |
| 564 self.assertEqual(self._getTodos(self.result), 1) | |
| 565 | |
| 566 | |
| 567 def test_success(self): | |
| 568 """ | |
| 569 A test run is still successful even if there are expected failures. | |
| 570 """ | |
| 571 self.result.addExpectedFailure(self.test, Failure(Exception()), | |
| 572 makeTodo('todo!')) | |
| 573 self.assertEqual(True, self.result.wasSuccessful()) | |
| 574 | |
| 575 | |
| 576 def test_unexpectedSuccess(self): | |
| 577 """ | |
| 578 A test which is marked as todo but succeeds will have an unexpected | |
| 579 success reported to its result. A test run is still successful even | |
| 580 when this happens. | |
| 581 """ | |
| 582 self.result.addUnexpectedSuccess(self.test, makeTodo("Heya!")) | |
| 583 self.assertEqual(True, self.result.wasSuccessful()) | |
| 584 self.assertEqual(self._getUnexpectedSuccesses(self.result), 1) | |
| 585 | |
| 586 | |
| 587 def test_summary(self): | |
| 588 """ | |
| 589 The reporter's C{printSummary} method should print the number of | |
| 590 expected failures that occured. | |
| 591 """ | |
| 592 self.result.addExpectedFailure(self.test, Failure(Exception()), | |
| 593 makeTodo('some reason')) | |
| 594 self.result.done() | |
| 595 output = self.stream.getvalue().splitlines()[-1] | |
| 596 prefix = 'PASSED ' | |
| 597 self.failUnless(output.startswith(prefix)) | |
| 598 self.assertEqual(output[len(prefix):].strip(), | |
| 599 '(expectedFailures=1)') | |
| 600 | |
| 601 | |
| 602 def test_basicErrors(self): | |
| 603 """ | |
| 604 The reporter's L{printErrors} method should include the value of the | |
| 605 Todo. | |
| 606 """ | |
| 607 self.result.addExpectedFailure(self.test, Failure(Exception()), | |
| 608 makeTodo('some reason')) | |
| 609 self.result.done() | |
| 610 output = self.stream.getvalue().splitlines()[4].strip() | |
| 611 self.assertEqual(output, "Reason: 'some reason'") | |
| 612 | |
| 613 | |
| 614 def test_booleanTodo(self): | |
| 615 """ | |
| 616 Booleans CAN'T be used as the value of a todo. Maybe this sucks. This | |
| 617 is a test for current behavior, not a requirement. | |
| 618 """ | |
| 619 self.result.addExpectedFailure(self.test, Failure(Exception()), | |
| 620 makeTodo(True)) | |
| 621 self.assertRaises(Exception, self.result.done) | |
| 622 | |
| 623 | |
| 624 def test_exceptionTodo(self): | |
| 625 """ | |
| 626 The exception for expected failures should be shown in the | |
| 627 C{printErrors} output. | |
| 628 """ | |
| 629 try: | |
| 630 1/0 | |
| 631 except Exception, e: | |
| 632 error = e | |
| 633 self.result.addExpectedFailure(self.test, Failure(error), | |
| 634 makeTodo("todo!")) | |
| 635 self.result.done() | |
| 636 output = '\n'.join(self.stream.getvalue().splitlines()[3:]).strip() | |
| 637 self.assertTrue(str(e) in output) | |
| 638 | |
| 639 | |
| 640 | |
| 641 class UncleanWarningTodoTest(TodoTest): | |
| 642 """ | |
| 643 Tests for L{UncleanWarningsReporterWrapper}'s handling of todos. | |
| 644 """ | |
| 645 | |
| 646 def setUp(self): | |
| 647 TodoTest.setUp(self) | |
| 648 self.result = UncleanWarningsReporterWrapper(self.result) | |
| 649 | |
| 650 | |
| 651 def _getTodos(self, result): | |
| 652 """ | |
| 653 Get the number of todos that happened to a reporter inside of an | |
| 654 unclean warnings reporter wrapper. | |
| 655 """ | |
| 656 return len(result._originalReporter.expectedFailures) | |
| 657 | |
| 658 | |
| 659 def _getUnexpectedSuccesses(self, result): | |
| 660 """ | |
| 661 Get the number of unexpected successes that happened to a reporter | |
| 662 inside of an unclean warnings reporter wrapper. | |
| 663 """ | |
| 664 return len(result._originalReporter.unexpectedSuccesses) | |
| 665 | |
| 666 | |
| 667 | |
| 668 class MockColorizer: | |
| 669 """ | |
| 670 Used by TestTreeReporter to make sure that output is colored correctly. | |
| 671 """ | |
| 672 def __init__(self, stream): | |
| 673 self.log = [] | |
| 674 | |
| 675 def supported(self): | |
| 676 return True | |
| 677 supported = classmethod(supported) | |
| 678 | |
| 679 def write(self, text, color): | |
| 680 self.log.append((color, text)) | |
| 681 | |
| 682 | |
| 683 class TestTreeReporter(unittest.TestCase): | |
| 684 def setUp(self): | |
| 685 from twisted.trial.test import sample | |
| 686 self.test = sample.FooTest('test_foo') | |
| 687 self.stream = StringIO.StringIO() | |
| 688 self.result = reporter.TreeReporter(self.stream) | |
| 689 self.result._colorizer = MockColorizer(self.stream) | |
| 690 self.log = self.result._colorizer.log | |
| 691 | |
| 692 def makeError(self): | |
| 693 try: | |
| 694 1/0 | |
| 695 except ZeroDivisionError: | |
| 696 f = Failure() | |
| 697 return f | |
| 698 | |
| 699 def test_cleanupError(self): | |
| 700 """ | |
| 701 Run cleanupErrors and check that the output is correct, and colored | |
| 702 correctly. | |
| 703 """ | |
| 704 f = self.makeError() | |
| 705 self.result.cleanupErrors(f) | |
| 706 color, text = self.log[0] | |
| 707 self.assertEqual(color.strip(), self.result.ERROR) | |
| 708 self.assertEqual(text.strip(), 'cleanup errors') | |
| 709 color, text = self.log[1] | |
| 710 self.assertEqual(color.strip(), self.result.ERROR) | |
| 711 self.assertEqual(text.strip(), '[ERROR]') | |
| 712 test_cleanupError = suppressWarnings( | |
| 713 test_cleanupError, | |
| 714 util.suppress(category=reporter.BrokenTestCaseWarning), | |
| 715 util.suppress(category=DeprecationWarning)) | |
| 716 | |
| 717 | |
| 718 def test_upDownError(self): | |
| 719 """ | |
| 720 Run upDownError and check that the output is correct and colored | |
| 721 correctly. | |
| 722 """ | |
| 723 self.result.upDownError("method", None, None, False) | |
| 724 color, text = self.log[0] | |
| 725 self.assertEqual(color.strip(), self.result.ERROR) | |
| 726 self.assertEqual(text.strip(), 'method') | |
| 727 test_upDownError = suppressWarnings( | |
| 728 test_upDownError, | |
| 729 util.suppress(category=DeprecationWarning, | |
| 730 message="upDownError is deprecated in Twisted 8.0.")) | |
| 731 | |
| 732 | |
| 733 def test_summaryColoredSuccess(self): | |
| 734 """ | |
| 735 The summary in case of success should have a good count of successes | |
| 736 and be colored properly. | |
| 737 """ | |
| 738 self.result.addSuccess(self.test) | |
| 739 self.result.done() | |
| 740 self.assertEquals(self.log[1], (self.result.SUCCESS, 'PASSED')) | |
| 741 self.assertEquals( | |
| 742 self.stream.getvalue().splitlines()[-1].strip(), "(successes=1)") | |
| 743 | |
| 744 | |
| 745 def test_summaryColoredFailure(self): | |
| 746 """ | |
| 747 The summary in case of failure should have a good count of errors | |
| 748 and be colored properly. | |
| 749 """ | |
| 750 try: | |
| 751 raise RuntimeError('foo') | |
| 752 except RuntimeError, excValue: | |
| 753 self.result.addError(self, sys.exc_info()) | |
| 754 self.result.done() | |
| 755 self.assertEquals(self.log[1], (self.result.FAILURE, 'FAILED')) | |
| 756 self.assertEquals( | |
| 757 self.stream.getvalue().splitlines()[-1].strip(), "(errors=1)") | |
| 758 | |
| 759 | |
| 760 def test_getPrelude(self): | |
| 761 """ | |
| 762 The tree needs to get the segments of the test ID that correspond | |
| 763 to the module and class that it belongs to. | |
| 764 """ | |
| 765 self.assertEqual( | |
| 766 ['foo.bar', 'baz'], | |
| 767 self.result._getPreludeSegments('foo.bar.baz.qux')) | |
| 768 self.assertEqual( | |
| 769 ['foo', 'bar'], | |
| 770 self.result._getPreludeSegments('foo.bar.baz')) | |
| 771 self.assertEqual( | |
| 772 ['foo'], | |
| 773 self.result._getPreludeSegments('foo.bar')) | |
| 774 self.assertEqual([], self.result._getPreludeSegments('foo')) | |
| 775 | |
| 776 | |
| 777 class TestReporter(unittest.TestCase): | |
| 778 resultFactory = reporter.Reporter | |
| 779 | |
| 780 def setUp(self): | |
| 781 from twisted.trial.test import sample | |
| 782 self.test = sample.FooTest('test_foo') | |
| 783 self.stream = StringIO.StringIO() | |
| 784 self.result = self.resultFactory(self.stream) | |
| 785 self._timer = 0 | |
| 786 self.result._getTime = self._getTime | |
| 787 | |
| 788 def _getTime(self): | |
| 789 self._timer += 1 | |
| 790 return self._timer | |
| 791 | |
| 792 def test_startStop(self): | |
| 793 self.result.startTest(self.test) | |
| 794 self.result.stopTest(self.test) | |
| 795 self.assertTrue(self.result._lastTime > 0) | |
| 796 self.assertEqual(self.result.testsRun, 1) | |
| 797 self.assertEqual(self.result.wasSuccessful(), True) | |
| 798 | |
| 799 | |
| 800 def test_brokenStream(self): | |
| 801 """ | |
| 802 Test that the reporter safely writes to its stream. | |
| 803 """ | |
| 804 result = self.resultFactory(stream=BrokenStream(self.stream)) | |
| 805 result._writeln("Hello") | |
| 806 self.assertEqual(self.stream.getvalue(), 'Hello\n') | |
| 807 self.stream.truncate(0) | |
| 808 result._writeln("Hello %s!", 'World') | |
| 809 self.assertEqual(self.stream.getvalue(), 'Hello World!\n') | |
| 810 | |
| 811 | |
| 812 | |
| 813 def test_printErrorsDeprecated(self): | |
| 814 """ | |
| 815 L{IReporter.printErrors} was deprecated in Twisted 8.0. | |
| 816 """ | |
| 817 def f(): | |
| 818 self.result.printErrors() | |
| 819 self.assertWarns( | |
| 820 DeprecationWarning, "printErrors is deprecated in Twisted 8.0.", | |
| 821 __file__, f) | |
| 822 | |
| 823 | |
| 824 def test_printSummaryDeprecated(self): | |
| 825 """ | |
| 826 L{IReporter.printSummary} was deprecated in Twisted 8.0. | |
| 827 """ | |
| 828 def f(): | |
| 829 self.result.printSummary() | |
| 830 self.assertWarns( | |
| 831 DeprecationWarning, "printSummary is deprecated in Twisted 8.0.", | |
| 832 __file__, f) | |
| 833 | |
| 834 | |
| 835 def test_writeDeprecated(self): | |
| 836 """ | |
| 837 L{IReporter.write} was deprecated in Twisted 8.0. | |
| 838 """ | |
| 839 def f(): | |
| 840 self.result.write("") | |
| 841 self.assertWarns( | |
| 842 DeprecationWarning, "write is deprecated in Twisted 8.0.", | |
| 843 __file__, f) | |
| 844 | |
| 845 | |
| 846 def test_writelnDeprecated(self): | |
| 847 """ | |
| 848 L{IReporter.writeln} was deprecated in Twisted 8.0. | |
| 849 """ | |
| 850 def f(): | |
| 851 self.result.writeln("") | |
| 852 self.assertWarns( | |
| 853 DeprecationWarning, "writeln is deprecated in Twisted 8.0.", | |
| 854 __file__, f) | |
| 855 | |
| 856 | |
| 857 def test_separatorDeprecated(self): | |
| 858 """ | |
| 859 L{IReporter.separator} was deprecated in Twisted 8.0. | |
| 860 """ | |
| 861 def f(): | |
| 862 return self.result.separator | |
| 863 self.assertWarns( | |
| 864 DeprecationWarning, "separator is deprecated in Twisted 8.0.", | |
| 865 __file__, f) | |
| 866 | |
| 867 | |
| 868 def test_streamDeprecated(self): | |
| 869 """ | |
| 870 L{IReporter.stream} was deprecated in Twisted 8.0. | |
| 871 """ | |
| 872 def f(): | |
| 873 return self.result.stream | |
| 874 self.assertWarns( | |
| 875 DeprecationWarning, "stream is deprecated in Twisted 8.0.", | |
| 876 __file__, f) | |
| 877 | |
| 878 | |
| 879 def test_upDownErrorDeprecated(self): | |
| 880 """ | |
| 881 L{IReporter.upDownError} was deprecated in Twisted 8.0. | |
| 882 """ | |
| 883 def f(): | |
| 884 self.result.upDownError(None, None, None, None) | |
| 885 self.assertWarns( | |
| 886 DeprecationWarning, "upDownError is deprecated in Twisted 8.0.", | |
| 887 __file__, f) | |
| 888 | |
| 889 | |
| 890 | |
| 891 class TestSafeStream(unittest.TestCase): | |
| 892 def test_safe(self): | |
| 893 """ | |
| 894 Test that L{reporter.SafeStream} successfully write to its original | |
| 895 stream even if an interrupt happens during the write. | |
| 896 """ | |
| 897 stream = StringIO.StringIO() | |
| 898 broken = BrokenStream(stream) | |
| 899 safe = reporter.SafeStream(broken) | |
| 900 safe.write("Hello") | |
| 901 self.assertEqual(stream.getvalue(), "Hello") | |
| 902 | |
| 903 | |
| 904 class TestTimingReporter(TestReporter): | |
| 905 resultFactory = reporter.TimingTextReporter | |
| 906 | |
| 907 | |
| 908 | |
| 909 class LoggingReporter(reporter.Reporter): | |
| 910 """ | |
| 911 Simple reporter that stores the last test that was passed to it. | |
| 912 """ | |
| 913 | |
| 914 def __init__(self, *args, **kwargs): | |
| 915 reporter.Reporter.__init__(self, *args, **kwargs) | |
| 916 self.test = None | |
| 917 | |
| 918 def addError(self, test, error): | |
| 919 self.test = test | |
| 920 | |
| 921 def addExpectedFailure(self, test, failure, todo): | |
| 922 self.test = test | |
| 923 | |
| 924 def addFailure(self, test, failure): | |
| 925 self.test = test | |
| 926 | |
| 927 def addSkip(self, test, skip): | |
| 928 self.test = test | |
| 929 | |
| 930 def addUnexpectedSuccess(self, test, todo): | |
| 931 self.test = test | |
| 932 | |
| 933 def startTest(self, test): | |
| 934 self.test = test | |
| 935 | |
| 936 def stopTest(self, test): | |
| 937 self.test = test | |
| 938 | |
| 939 | |
| 940 | |
| 941 class TestAdaptedReporter(unittest.TestCase): | |
| 942 """ | |
| 943 L{reporter._AdaptedReporter} is a reporter wrapper that wraps all of the | |
| 944 tests it receives before passing them on to the original reporter. | |
| 945 """ | |
| 946 | |
| 947 def setUp(self): | |
| 948 self.wrappedResult = self.getWrappedResult() | |
| 949 | |
| 950 | |
| 951 def _testAdapter(self, test): | |
| 952 return test.id() | |
| 953 | |
| 954 | |
| 955 def assertWrapped(self, wrappedResult, test): | |
| 956 self.assertEqual(wrappedResult._originalReporter.test, self._testAdapter
(test)) | |
| 957 | |
| 958 | |
| 959 def getFailure(self, exceptionInstance): | |
| 960 """ | |
| 961 Return a L{Failure} from raising the given exception. | |
| 962 | |
| 963 @param exceptionInstance: The exception to raise. | |
| 964 @return: L{Failure} | |
| 965 """ | |
| 966 try: | |
| 967 raise exceptionInstance | |
| 968 except: | |
| 969 return Failure() | |
| 970 | |
| 971 | |
| 972 def getWrappedResult(self): | |
| 973 result = LoggingReporter() | |
| 974 return reporter._AdaptedReporter(result, self._testAdapter) | |
| 975 | |
| 976 | |
| 977 def test_addError(self): | |
| 978 """ | |
| 979 C{addError} wraps its test with the provided adapter. | |
| 980 """ | |
| 981 self.wrappedResult.addError(self, self.getFailure(RuntimeError())) | |
| 982 self.assertWrapped(self.wrappedResult, self) | |
| 983 | |
| 984 | |
| 985 def test_addFailure(self): | |
| 986 """ | |
| 987 C{addFailure} wraps its test with the provided adapter. | |
| 988 """ | |
| 989 self.wrappedResult.addFailure(self, self.getFailure(AssertionError())) | |
| 990 self.assertWrapped(self.wrappedResult, self) | |
| 991 | |
| 992 | |
| 993 def test_addSkip(self): | |
| 994 """ | |
| 995 C{addSkip} wraps its test with the provided adapter. | |
| 996 """ | |
| 997 self.wrappedResult.addSkip(self, self.getFailure(SkipTest('no reason'))) | |
| 998 self.assertWrapped(self.wrappedResult, self) | |
| 999 | |
| 1000 | |
| 1001 def test_startTest(self): | |
| 1002 """ | |
| 1003 C{startTest} wraps its test with the provided adapter. | |
| 1004 """ | |
| 1005 self.wrappedResult.startTest(self) | |
| 1006 self.assertWrapped(self.wrappedResult, self) | |
| 1007 | |
| 1008 | |
| 1009 def test_stopTest(self): | |
| 1010 """ | |
| 1011 C{stopTest} wraps its test with the provided adapter. | |
| 1012 """ | |
| 1013 self.wrappedResult.stopTest(self) | |
| 1014 self.assertWrapped(self.wrappedResult, self) | |
| 1015 | |
| 1016 | |
| 1017 def test_addExpectedFailure(self): | |
| 1018 """ | |
| 1019 C{addExpectedFailure} wraps its test with the provided adapter. | |
| 1020 """ | |
| 1021 self.wrappedResult.addExpectedFailure( | |
| 1022 self, self.getFailure(RuntimeError()), Todo("no reason")) | |
| 1023 self.assertWrapped(self.wrappedResult, self) | |
| 1024 | |
| 1025 | |
| 1026 def test_addUnexpectedSuccess(self): | |
| 1027 """ | |
| 1028 C{addUnexpectedSuccess} wraps its test with the provided adapter. | |
| 1029 """ | |
| 1030 self.wrappedResult.addUnexpectedSuccess(self, Todo("no reason")) | |
| 1031 self.assertWrapped(self.wrappedResult, self) | |
| OLD | NEW |