| OLD | NEW |
| (Empty) |
| 1 # -*- test-case-name: twisted.trial.test.test_tests -*- | |
| 2 # | |
| 3 # Copyright (c) 2001-2007 Twisted Matrix Laboratories. | |
| 4 # See LICENSE for details. | |
| 5 | |
| 6 """ | |
| 7 Things likely to be used by writers of unit tests. | |
| 8 | |
| 9 Maintainer: Jonathan Lange <jml@twistedmatrix.com> | |
| 10 """ | |
| 11 | |
| 12 | |
| 13 import doctest | |
| 14 import os, warnings, sys, tempfile, gc | |
| 15 from pprint import pformat | |
| 16 | |
| 17 from twisted.internet import defer, utils | |
| 18 from twisted.python import components, failure, log, monkey | |
| 19 from twisted.python.compat import set | |
| 20 from twisted.python import deprecate | |
| 21 from twisted.python.deprecate import getDeprecationWarningString | |
| 22 | |
| 23 from twisted.trial import itrial, reporter, util | |
| 24 | |
| 25 pyunit = __import__('unittest') | |
| 26 | |
| 27 from zope.interface import implements | |
| 28 | |
| 29 | |
| 30 | |
| 31 class SkipTest(Exception): | |
| 32 """ | |
| 33 Raise this (with a reason) to skip the current test. You may also set | |
| 34 method.skip to a reason string to skip it, or set class.skip to skip the | |
| 35 entire TestCase. | |
| 36 """ | |
| 37 | |
| 38 | |
| 39 class FailTest(AssertionError): | |
| 40 """Raised to indicate the current test has failed to pass.""" | |
| 41 | |
| 42 | |
| 43 class Todo(object): | |
| 44 """ | |
| 45 Internal object used to mark a L{TestCase} as 'todo'. Tests marked 'todo' | |
| 46 are reported differently in Trial L{TestResult}s. If todo'd tests fail, | |
| 47 they do not fail the suite and the errors are reported in a separate | |
| 48 category. If todo'd tests succeed, Trial L{TestResult}s will report an | |
| 49 unexpected success. | |
| 50 """ | |
| 51 | |
| 52 def __init__(self, reason, errors=None): | |
| 53 """ | |
| 54 @param reason: A string explaining why the test is marked 'todo' | |
| 55 | |
| 56 @param errors: An iterable of exception types that the test is | |
| 57 expected to raise. If one of these errors is raised by the test, it | |
| 58 will be trapped. Raising any other kind of error will fail the test. | |
| 59 If C{None} is passed, then all errors will be trapped. | |
| 60 """ | |
| 61 self.reason = reason | |
| 62 self.errors = errors | |
| 63 | |
| 64 def __repr__(self): | |
| 65 return "<Todo reason=%r errors=%r>" % (self.reason, self.errors) | |
| 66 | |
| 67 def expected(self, failure): | |
| 68 """ | |
| 69 @param failure: A L{twisted.python.failure.Failure}. | |
| 70 | |
| 71 @return: C{True} if C{failure} is expected, C{False} otherwise. | |
| 72 """ | |
| 73 if self.errors is None: | |
| 74 return True | |
| 75 for error in self.errors: | |
| 76 if failure.check(error): | |
| 77 return True | |
| 78 return False | |
| 79 | |
| 80 | |
| 81 def makeTodo(value): | |
| 82 """ | |
| 83 Return a L{Todo} object built from C{value}. | |
| 84 | |
| 85 If C{value} is a string, return a Todo that expects any exception with | |
| 86 C{value} as a reason. If C{value} is a tuple, the second element is used | |
| 87 as the reason and the first element as the excepted error(s). | |
| 88 | |
| 89 @param value: A string or a tuple of C{(errors, reason)}, where C{errors} | |
| 90 is either a single exception class or an iterable of exception classes. | |
| 91 | |
| 92 @return: A L{Todo} object. | |
| 93 """ | |
| 94 if isinstance(value, str): | |
| 95 return Todo(reason=value) | |
| 96 if isinstance(value, tuple): | |
| 97 errors, reason = value | |
| 98 try: | |
| 99 errors = list(errors) | |
| 100 except TypeError: | |
| 101 errors = [errors] | |
| 102 return Todo(reason=reason, errors=errors) | |
| 103 | |
| 104 | |
| 105 class _Assertions(pyunit.TestCase, object): | |
| 106 """ | |
| 107 Replaces many of the built-in TestCase assertions. In general, these | |
| 108 assertions provide better error messages and are easier to use in | |
| 109 callbacks. Also provides new assertions such as L{failUnlessFailure}. | |
| 110 | |
| 111 Although the tests are defined as 'failIf*' and 'failUnless*', they can | |
| 112 also be called as 'assertNot*' and 'assert*'. | |
| 113 """ | |
| 114 | |
| 115 def fail(self, msg=None): | |
| 116 """absolutely fails the test, do not pass go, do not collect $200 | |
| 117 | |
| 118 @param msg: the message that will be displayed as the reason for the | |
| 119 failure | |
| 120 """ | |
| 121 raise self.failureException(msg) | |
| 122 | |
| 123 def failIf(self, condition, msg=None): | |
| 124 """fails the test if C{condition} evaluates to False | |
| 125 | |
| 126 @param condition: any object that defines __nonzero__ | |
| 127 """ | |
| 128 if condition: | |
| 129 raise self.failureException(msg) | |
| 130 return condition | |
| 131 assertNot = assertFalse = failUnlessFalse = failIf | |
| 132 | |
| 133 def failUnless(self, condition, msg=None): | |
| 134 """fails the test if C{condition} evaluates to True | |
| 135 | |
| 136 @param condition: any object that defines __nonzero__ | |
| 137 """ | |
| 138 if not condition: | |
| 139 raise self.failureException(msg) | |
| 140 return condition | |
| 141 assert_ = assertTrue = failUnlessTrue = failUnless | |
| 142 | |
| 143 def failUnlessRaises(self, exception, f, *args, **kwargs): | |
| 144 """fails the test unless calling the function C{f} with the given C{args
} | |
| 145 and C{kwargs} raises C{exception}. The failure will report the | |
| 146 traceback and call stack of the unexpected exception. | |
| 147 | |
| 148 @param exception: exception type that is to be expected | |
| 149 @param f: the function to call | |
| 150 | |
| 151 @return: The raised exception instance, if it is of the given type. | |
| 152 @raise self.failureException: Raised if the function call does not raise
an exception | |
| 153 or if it raises an exception of a different type. | |
| 154 """ | |
| 155 try: | |
| 156 result = f(*args, **kwargs) | |
| 157 except exception, inst: | |
| 158 return inst | |
| 159 except: | |
| 160 raise self.failureException('%s raised instead of %s:\n %s' | |
| 161 % (sys.exc_info()[0], | |
| 162 exception.__name__, | |
| 163 failure.Failure().getTraceback())) | |
| 164 else: | |
| 165 raise self.failureException('%s not raised (%r returned)' | |
| 166 % (exception.__name__, result)) | |
| 167 assertRaises = failUnlessRaises | |
| 168 | |
| 169 def failUnlessEqual(self, first, second, msg=''): | |
| 170 """ | |
| 171 Fail the test if C{first} and C{second} are not equal. | |
| 172 | |
| 173 @param msg: A string describing the failure that's included in the | |
| 174 exception. | |
| 175 """ | |
| 176 if not first == second: | |
| 177 if msg is None: | |
| 178 msg = '' | |
| 179 if len(msg) > 0: | |
| 180 msg += '\n' | |
| 181 raise self.failureException( | |
| 182 '%snot equal:\na = %s\nb = %s\n' | |
| 183 % (msg, pformat(first), pformat(second))) | |
| 184 return first | |
| 185 assertEqual = assertEquals = failUnlessEquals = failUnlessEqual | |
| 186 | |
| 187 def failUnlessIdentical(self, first, second, msg=None): | |
| 188 """fail the test if C{first} is not C{second}. This is an | |
| 189 obect-identity-equality test, not an object equality (i.e. C{__eq__}) te
st | |
| 190 | |
| 191 @param msg: if msg is None, then the failure message will be | |
| 192 '%r is not %r' % (first, second) | |
| 193 """ | |
| 194 if first is not second: | |
| 195 raise self.failureException(msg or '%r is not %r' % (first, second)) | |
| 196 return first | |
| 197 assertIdentical = failUnlessIdentical | |
| 198 | |
| 199 def failIfIdentical(self, first, second, msg=None): | |
| 200 """fail the test if C{first} is C{second}. This is an | |
| 201 obect-identity-equality test, not an object equality (i.e. C{__eq__}) te
st | |
| 202 | |
| 203 @param msg: if msg is None, then the failure message will be | |
| 204 '%r is %r' % (first, second) | |
| 205 """ | |
| 206 if first is second: | |
| 207 raise self.failureException(msg or '%r is %r' % (first, second)) | |
| 208 return first | |
| 209 assertNotIdentical = failIfIdentical | |
| 210 | |
| 211 def failIfEqual(self, first, second, msg=None): | |
| 212 """fail the test if C{first} == C{second} | |
| 213 | |
| 214 @param msg: if msg is None, then the failure message will be | |
| 215 '%r == %r' % (first, second) | |
| 216 """ | |
| 217 if not first != second: | |
| 218 raise self.failureException(msg or '%r == %r' % (first, second)) | |
| 219 return first | |
| 220 assertNotEqual = assertNotEquals = failIfEquals = failIfEqual | |
| 221 | |
| 222 def failUnlessIn(self, containee, container, msg=None): | |
| 223 """fail the test if C{containee} is not found in C{container} | |
| 224 | |
| 225 @param containee: the value that should be in C{container} | |
| 226 @param container: a sequence type, or in the case of a mapping type, | |
| 227 will follow semantics of 'if key in dict.keys()' | |
| 228 @param msg: if msg is None, then the failure message will be | |
| 229 '%r not in %r' % (first, second) | |
| 230 """ | |
| 231 if containee not in container: | |
| 232 raise self.failureException(msg or "%r not in %r" | |
| 233 % (containee, container)) | |
| 234 return containee | |
| 235 assertIn = failUnlessIn | |
| 236 | |
| 237 def failIfIn(self, containee, container, msg=None): | |
| 238 """fail the test if C{containee} is found in C{container} | |
| 239 | |
| 240 @param containee: the value that should not be in C{container} | |
| 241 @param container: a sequence type, or in the case of a mapping type, | |
| 242 will follow semantics of 'if key in dict.keys()' | |
| 243 @param msg: if msg is None, then the failure message will be | |
| 244 '%r in %r' % (first, second) | |
| 245 """ | |
| 246 if containee in container: | |
| 247 raise self.failureException(msg or "%r in %r" | |
| 248 % (containee, container)) | |
| 249 return containee | |
| 250 assertNotIn = failIfIn | |
| 251 | |
| 252 def failIfAlmostEqual(self, first, second, places=7, msg=None): | |
| 253 """Fail if the two objects are equal as determined by their | |
| 254 difference rounded to the given number of decimal places | |
| 255 (default 7) and comparing to zero. | |
| 256 | |
| 257 @note: decimal places (from zero) is usually not the same | |
| 258 as significant digits (measured from the most | |
| 259 signficant digit). | |
| 260 | |
| 261 @note: included for compatiblity with PyUnit test cases | |
| 262 """ | |
| 263 if round(second-first, places) == 0: | |
| 264 raise self.failureException(msg or '%r == %r within %r places' | |
| 265 % (first, second, places)) | |
| 266 return first | |
| 267 assertNotAlmostEqual = assertNotAlmostEquals = failIfAlmostEqual | |
| 268 failIfAlmostEquals = failIfAlmostEqual | |
| 269 | |
| 270 def failUnlessAlmostEqual(self, first, second, places=7, msg=None): | |
| 271 """Fail if the two objects are unequal as determined by their | |
| 272 difference rounded to the given number of decimal places | |
| 273 (default 7) and comparing to zero. | |
| 274 | |
| 275 @note: decimal places (from zero) is usually not the same | |
| 276 as significant digits (measured from the most | |
| 277 signficant digit). | |
| 278 | |
| 279 @note: included for compatiblity with PyUnit test cases | |
| 280 """ | |
| 281 if round(second-first, places) != 0: | |
| 282 raise self.failureException(msg or '%r != %r within %r places' | |
| 283 % (first, second, places)) | |
| 284 return first | |
| 285 assertAlmostEqual = assertAlmostEquals = failUnlessAlmostEqual | |
| 286 failUnlessAlmostEquals = failUnlessAlmostEqual | |
| 287 | |
| 288 def failUnlessApproximates(self, first, second, tolerance, msg=None): | |
| 289 """asserts that C{first} - C{second} > C{tolerance} | |
| 290 | |
| 291 @param msg: if msg is None, then the failure message will be | |
| 292 '%r ~== %r' % (first, second) | |
| 293 """ | |
| 294 if abs(first - second) > tolerance: | |
| 295 raise self.failureException(msg or "%s ~== %s" % (first, second)) | |
| 296 return first | |
| 297 assertApproximates = failUnlessApproximates | |
| 298 | |
| 299 def failUnlessFailure(self, deferred, *expectedFailures): | |
| 300 """Assert that C{deferred} will errback with one of | |
| 301 C{expectedFailures}. Returns the original Deferred with callbacks | |
| 302 added. You will need to return this Deferred from your test case. | |
| 303 """ | |
| 304 def _cb(ignore): | |
| 305 raise self.failureException( | |
| 306 "did not catch an error, instead got %r" % (ignore,)) | |
| 307 | |
| 308 def _eb(failure): | |
| 309 if failure.check(*expectedFailures): | |
| 310 return failure.value | |
| 311 else: | |
| 312 output = ('\nExpected: %r\nGot:\n%s' | |
| 313 % (expectedFailures, str(failure))) | |
| 314 raise self.failureException(output) | |
| 315 return deferred.addCallbacks(_cb, _eb) | |
| 316 assertFailure = failUnlessFailure | |
| 317 | |
| 318 def failUnlessSubstring(self, substring, astring, msg=None): | |
| 319 return self.failUnlessIn(substring, astring, msg) | |
| 320 assertSubstring = failUnlessSubstring | |
| 321 | |
| 322 def failIfSubstring(self, substring, astring, msg=None): | |
| 323 return self.failIfIn(substring, astring, msg) | |
| 324 assertNotSubstring = failIfSubstring | |
| 325 | |
| 326 def failUnlessWarns(self, category, message, filename, f, | |
| 327 *args, **kwargs): | |
| 328 """ | |
| 329 Fail if the given function doesn't generate the specified warning when | |
| 330 called. It calls the function, checks the warning, and forwards the | |
| 331 result of the function if everything is fine. | |
| 332 | |
| 333 @param category: the category of the warning to check. | |
| 334 @param message: the output message of the warning to check. | |
| 335 @param filename: the filename where the warning should come from. | |
| 336 @param f: the function which is supposed to generate the warning. | |
| 337 @type f: any callable. | |
| 338 @param args: the arguments to C{f}. | |
| 339 @param kwargs: the keywords arguments to C{f}. | |
| 340 | |
| 341 @return: the result of the original function C{f}. | |
| 342 """ | |
| 343 warningsShown = [] | |
| 344 def warnExplicit(*args): | |
| 345 warningsShown.append(args) | |
| 346 | |
| 347 origExplicit = warnings.warn_explicit | |
| 348 try: | |
| 349 warnings.warn_explicit = warnExplicit | |
| 350 result = f(*args, **kwargs) | |
| 351 finally: | |
| 352 warnings.warn_explicit = origExplicit | |
| 353 | |
| 354 if not warningsShown: | |
| 355 self.fail("No warnings emitted") | |
| 356 first = warningsShown[0] | |
| 357 for other in warningsShown[1:]: | |
| 358 if other[:2] != first[:2]: | |
| 359 self.fail("Can't handle different warnings") | |
| 360 gotMessage, gotCategory, gotFilename, lineno = first[:4] | |
| 361 self.assertEqual(gotMessage, message) | |
| 362 self.assertIdentical(gotCategory, category) | |
| 363 | |
| 364 # Use starts with because of .pyc/.pyo issues. | |
| 365 self.failUnless( | |
| 366 filename.startswith(gotFilename), | |
| 367 'Warning in %r, expected %r' % (gotFilename, filename)) | |
| 368 | |
| 369 # It would be nice to be able to check the line number as well, but | |
| 370 # different configurations actually end up reporting different line | |
| 371 # numbers (generally the variation is only 1 line, but that's enough | |
| 372 # to fail the test erroneously...). | |
| 373 # self.assertEqual(lineno, xxx) | |
| 374 | |
| 375 return result | |
| 376 assertWarns = failUnlessWarns | |
| 377 | |
| 378 def failUnlessIsInstance(self, instance, classOrTuple): | |
| 379 """ | |
| 380 Assert that the given instance is of the given class or of one of the | |
| 381 given classes. | |
| 382 | |
| 383 @param instance: the object to test the type (first argument of the | |
| 384 C{isinstance} call). | |
| 385 @type instance: any. | |
| 386 @param classOrTuple: the class or classes to test against (second | |
| 387 argument of the C{isinstance} call). | |
| 388 @type classOrTuple: class, type, or tuple. | |
| 389 """ | |
| 390 if not isinstance(instance, classOrTuple): | |
| 391 self.fail("%r is not an instance of %s" % (instance, classOrTuple)) | |
| 392 | |
| 393 assertIsInstance = failUnlessIsInstance | |
| 394 | |
| 395 def failIfIsInstance(self, instance, classOrTuple): | |
| 396 """ | |
| 397 Assert that the given instance is not of the given class or of one of | |
| 398 the given classes. | |
| 399 | |
| 400 @param instance: the object to test the type (first argument of the | |
| 401 C{isinstance} call). | |
| 402 @type instance: any. | |
| 403 @param classOrTuple: the class or classes to test against (second | |
| 404 argument of the C{isinstance} call). | |
| 405 @type classOrTuple: class, type, or tuple. | |
| 406 """ | |
| 407 if isinstance(instance, classOrTuple): | |
| 408 self.fail("%r is not an instance of %s" % (instance, classOrTuple)) | |
| 409 | |
| 410 assertNotIsInstance = failIfIsInstance | |
| 411 | |
| 412 | |
| 413 class _LogObserver(object): | |
| 414 """ | |
| 415 Observes the Twisted logs and catches any errors. | |
| 416 """ | |
| 417 | |
| 418 def __init__(self): | |
| 419 self._errors = [] | |
| 420 self._added = 0 | |
| 421 self._ignored = [] | |
| 422 | |
| 423 def _add(self): | |
| 424 if self._added == 0: | |
| 425 log.addObserver(self.gotEvent) | |
| 426 self._oldFE, log._flushErrors = (log._flushErrors, self.flushErrors) | |
| 427 self._oldIE, log._ignore = (log._ignore, self._ignoreErrors) | |
| 428 self._oldCI, log._clearIgnores = (log._clearIgnores, | |
| 429 self._clearIgnores) | |
| 430 self._added += 1 | |
| 431 | |
| 432 def _remove(self): | |
| 433 self._added -= 1 | |
| 434 if self._added == 0: | |
| 435 log.removeObserver(self.gotEvent) | |
| 436 log._flushErrors = self._oldFE | |
| 437 log._ignore = self._oldIE | |
| 438 log._clearIgnores = self._oldCI | |
| 439 | |
| 440 def _ignoreErrors(self, *errorTypes): | |
| 441 """ | |
| 442 Do not store any errors with any of the given types. | |
| 443 """ | |
| 444 self._ignored.extend(errorTypes) | |
| 445 | |
| 446 def _clearIgnores(self): | |
| 447 """ | |
| 448 Stop ignoring any errors we might currently be ignoring. | |
| 449 """ | |
| 450 self._ignored = [] | |
| 451 | |
| 452 def flushErrors(self, *errorTypes): | |
| 453 """ | |
| 454 Flush errors from the list of caught errors. If no arguments are | |
| 455 specified, remove all errors. If arguments are specified, only remove | |
| 456 errors of those types from the stored list. | |
| 457 """ | |
| 458 if errorTypes: | |
| 459 flushed = [] | |
| 460 remainder = [] | |
| 461 for f in self._errors: | |
| 462 if f.check(*errorTypes): | |
| 463 flushed.append(f) | |
| 464 else: | |
| 465 remainder.append(f) | |
| 466 self._errors = remainder | |
| 467 else: | |
| 468 flushed = self._errors | |
| 469 self._errors = [] | |
| 470 return flushed | |
| 471 | |
| 472 def getErrors(self): | |
| 473 """ | |
| 474 Return a list of errors caught by this observer. | |
| 475 """ | |
| 476 return self._errors | |
| 477 | |
| 478 def gotEvent(self, event): | |
| 479 """ | |
| 480 The actual observer method. Called whenever a message is logged. | |
| 481 | |
| 482 @param event: A dictionary containing the log message. Actual | |
| 483 structure undocumented (see source for L{twisted.python.log}). | |
| 484 """ | |
| 485 if event.get('isError', False) and 'failure' in event: | |
| 486 f = event['failure'] | |
| 487 if len(self._ignored) == 0 or not f.check(*self._ignored): | |
| 488 self._errors.append(f) | |
| 489 | |
| 490 | |
| 491 _logObserver = _LogObserver() | |
| 492 | |
| 493 _wait_is_running = [] | |
| 494 | |
| 495 | |
| 496 class TestCase(_Assertions): | |
| 497 """ | |
| 498 A unit test. The atom of the unit testing universe. | |
| 499 | |
| 500 This class extends C{unittest.TestCase} from the standard library. The | |
| 501 main feature is the ability to return C{Deferred}s from tests and fixture | |
| 502 methods and to have the suite wait for those C{Deferred}s to fire. | |
| 503 | |
| 504 To write a unit test, subclass C{TestCase} and define a method (say, | |
| 505 'test_foo') on the subclass. To run the test, instantiate your subclass | |
| 506 with the name of the method, and call L{run} on the instance, passing a | |
| 507 L{TestResult} object. | |
| 508 | |
| 509 The C{trial} script will automatically find any C{TestCase} subclasses | |
| 510 defined in modules beginning with 'test_' and construct test cases for all | |
| 511 methods beginning with 'test'. | |
| 512 | |
| 513 If an error is logged during the test run, the test will fail with an | |
| 514 error. See L{log.err}. | |
| 515 | |
| 516 @ivar failureException: An exception class, defaulting to C{FailTest}. If | |
| 517 the test method raises this exception, it will be reported as a failure, | |
| 518 rather than an exception. All of the assertion methods raise this if the | |
| 519 assertion fails. | |
| 520 | |
| 521 @ivar skip: C{None} or a string explaining why this test is to be | |
| 522 skipped. If defined, the test will not be run. Instead, it will be | |
| 523 reported to the result object as 'skipped' (if the C{TestResult} supports | |
| 524 skipping). | |
| 525 | |
| 526 @ivar suppress: C{None} or a list of tuples of C{(args, kwargs)} to be | |
| 527 passed to C{warnings.filterwarnings}. Use these to suppress warnings | |
| 528 raised in a test. Useful for testing deprecated code. See also | |
| 529 L{util.suppress}. | |
| 530 | |
| 531 @ivar timeout: C{None} or a real number of seconds. If set, the test will | |
| 532 raise an error if it takes longer than C{timeout} seconds. | |
| 533 | |
| 534 @ivar todo: C{None}, a string or a tuple of C{(errors, reason)} where | |
| 535 C{errors} is either an exception class or an iterable of exception | |
| 536 classes, and C{reason} is a string. See L{Todo} or L{makeTodo} for more | |
| 537 information. | |
| 538 """ | |
| 539 | |
| 540 implements(itrial.ITestCase) | |
| 541 failureException = FailTest | |
| 542 | |
| 543 def __init__(self, methodName='runTest'): | |
| 544 """ | |
| 545 Construct an asynchronous test case for C{methodName}. | |
| 546 | |
| 547 @param methodName: The name of a method on C{self}. This method should | |
| 548 be a unit test. That is, it should be a short method that calls some of | |
| 549 the assert* methods. If C{methodName} is unspecified, L{runTest} will | |
| 550 be used as the test method. This is mostly useful for testing Trial. | |
| 551 """ | |
| 552 super(TestCase, self).__init__(methodName) | |
| 553 self._testMethodName = methodName | |
| 554 testMethod = getattr(self, methodName) | |
| 555 self._parents = [testMethod, self] | |
| 556 self._parents.extend(util.getPythonContainers(testMethod)) | |
| 557 self._shared = (hasattr(self, 'setUpClass') or | |
| 558 hasattr(self, 'tearDownClass')) | |
| 559 if self._shared: | |
| 560 self._prepareClassFixture() | |
| 561 if not hasattr(self.__class__, '_instances'): | |
| 562 self._initInstances() | |
| 563 self.__class__._instances.add(self) | |
| 564 self._passed = False | |
| 565 self._cleanups = [] | |
| 566 | |
| 567 def _initInstances(cls): | |
| 568 cls._instances = set() | |
| 569 cls._instancesRun = set() | |
| 570 _initInstances = classmethod(_initInstances) | |
| 571 | |
| 572 def _isFirst(self): | |
| 573 return len(self.__class__._instancesRun) == 0 | |
| 574 | |
| 575 def _isLast(self): | |
| 576 return self.__class__._instancesRun == self.__class__._instances | |
| 577 | |
| 578 def _prepareClassFixture(self): | |
| 579 """Lots of tests assume that test methods all run in the same instance | |
| 580 of TestCase. This isn't true. Calling this method ensures that | |
| 581 self.__class__._testCaseInstance contains an instance of this class | |
| 582 that will remain the same for all tests from this class. | |
| 583 """ | |
| 584 if not hasattr(self.__class__, '_testCaseInstance'): | |
| 585 self.__class__._testCaseInstance = self | |
| 586 if self.__class__._testCaseInstance.__class__ != self.__class__: | |
| 587 self.__class__._testCaseInstance = self | |
| 588 | |
| 589 def _run(self, methodName, result): | |
| 590 from twisted.internet import reactor | |
| 591 timeout = self.getTimeout() | |
| 592 def onTimeout(d): | |
| 593 e = defer.TimeoutError("%r (%s) still running at %s secs" | |
| 594 % (self, methodName, timeout)) | |
| 595 f = failure.Failure(e) | |
| 596 # try to errback the deferred that the test returns (for no gorram | |
| 597 # reason) (see issue1005 and test_errorPropagation in | |
| 598 # test_deferred) | |
| 599 try: | |
| 600 d.errback(f) | |
| 601 except defer.AlreadyCalledError: | |
| 602 # if the deferred has been called already but the *back chain | |
| 603 # is still unfinished, crash the reactor and report timeout | |
| 604 # error ourself. | |
| 605 reactor.crash() | |
| 606 self._timedOut = True # see self._wait | |
| 607 todo = self.getTodo() | |
| 608 if todo is not None and todo.expected(f): | |
| 609 result.addExpectedFailure(self, f, todo) | |
| 610 else: | |
| 611 result.addError(self, f) | |
| 612 onTimeout = utils.suppressWarnings( | |
| 613 onTimeout, util.suppress(category=DeprecationWarning)) | |
| 614 if self._shared: | |
| 615 test = self.__class__._testCaseInstance | |
| 616 else: | |
| 617 test = self | |
| 618 method = getattr(test, methodName) | |
| 619 d = defer.maybeDeferred(utils.runWithWarningsSuppressed, | |
| 620 self.getSuppress(), method) | |
| 621 call = reactor.callLater(timeout, onTimeout, d) | |
| 622 d.addBoth(lambda x : call.active() and call.cancel() or x) | |
| 623 return d | |
| 624 | |
| 625 def shortDescription(self): | |
| 626 desc = super(TestCase, self).shortDescription() | |
| 627 if desc is None: | |
| 628 return self._testMethodName | |
| 629 return desc | |
| 630 | |
| 631 def __call__(self, *args, **kwargs): | |
| 632 return self.run(*args, **kwargs) | |
| 633 | |
| 634 def deferSetUpClass(self, result): | |
| 635 if not hasattr(self, 'setUpClass'): | |
| 636 d = defer.succeed(None) | |
| 637 d.addCallback(self.deferSetUp, result) | |
| 638 return d | |
| 639 d = self._run('setUpClass', result) | |
| 640 d.addCallbacks(self.deferSetUp, self._ebDeferSetUpClass, | |
| 641 callbackArgs=(result,), | |
| 642 errbackArgs=(result,)) | |
| 643 return d | |
| 644 | |
| 645 def _ebDeferSetUpClass(self, error, result): | |
| 646 if error.check(SkipTest): | |
| 647 result.addSkip(self, self._getReason(error)) | |
| 648 self.__class__._instancesRun.remove(self) | |
| 649 elif error.check(KeyboardInterrupt): | |
| 650 result.stop() | |
| 651 else: | |
| 652 result.addError(self, error) | |
| 653 self.__class__._instancesRun.remove(self) | |
| 654 | |
| 655 def deferSetUp(self, ignored, result): | |
| 656 d = self._run('setUp', result) | |
| 657 d.addCallbacks(self.deferTestMethod, self._ebDeferSetUp, | |
| 658 callbackArgs=(result,), | |
| 659 errbackArgs=(result,)) | |
| 660 return d | |
| 661 | |
| 662 def _ebDeferSetUp(self, failure, result): | |
| 663 if failure.check(SkipTest): | |
| 664 result.addSkip(self, self._getReason(failure)) | |
| 665 else: | |
| 666 result.addError(self, failure) | |
| 667 if failure.check(KeyboardInterrupt): | |
| 668 result.stop() | |
| 669 return self.deferRunCleanups(None, result) | |
| 670 | |
| 671 def deferTestMethod(self, ignored, result): | |
| 672 d = self._run(self._testMethodName, result) | |
| 673 d.addCallbacks(self._cbDeferTestMethod, self._ebDeferTestMethod, | |
| 674 callbackArgs=(result,), | |
| 675 errbackArgs=(result,)) | |
| 676 d.addBoth(self.deferRunCleanups, result) | |
| 677 d.addBoth(self.deferTearDown, result) | |
| 678 if self._shared and hasattr(self, 'tearDownClass') and self._isLast(): | |
| 679 d.addBoth(self.deferTearDownClass, result) | |
| 680 return d | |
| 681 | |
| 682 def _cbDeferTestMethod(self, ignored, result): | |
| 683 if self.getTodo() is not None: | |
| 684 result.addUnexpectedSuccess(self, self.getTodo()) | |
| 685 else: | |
| 686 self._passed = True | |
| 687 return ignored | |
| 688 | |
| 689 def _ebDeferTestMethod(self, f, result): | |
| 690 todo = self.getTodo() | |
| 691 if todo is not None and todo.expected(f): | |
| 692 result.addExpectedFailure(self, f, todo) | |
| 693 elif f.check(self.failureException, FailTest): | |
| 694 result.addFailure(self, f) | |
| 695 elif f.check(KeyboardInterrupt): | |
| 696 result.addError(self, f) | |
| 697 result.stop() | |
| 698 elif f.check(SkipTest): | |
| 699 result.addSkip(self, self._getReason(f)) | |
| 700 else: | |
| 701 result.addError(self, f) | |
| 702 | |
| 703 def deferTearDown(self, ignored, result): | |
| 704 d = self._run('tearDown', result) | |
| 705 d.addErrback(self._ebDeferTearDown, result) | |
| 706 return d | |
| 707 | |
| 708 def _ebDeferTearDown(self, failure, result): | |
| 709 result.addError(self, failure) | |
| 710 if failure.check(KeyboardInterrupt): | |
| 711 result.stop() | |
| 712 self._passed = False | |
| 713 | |
| 714 def deferRunCleanups(self, ignored, result): | |
| 715 """ | |
| 716 Run any scheduled cleanups and report errors (if any to the result | |
| 717 object. | |
| 718 """ | |
| 719 d = self._runCleanups() | |
| 720 d.addCallback(self._cbDeferRunCleanups, result) | |
| 721 return d | |
| 722 | |
| 723 def _cbDeferRunCleanups(self, cleanupResults, result): | |
| 724 for flag, failure in cleanupResults: | |
| 725 if flag == defer.FAILURE: | |
| 726 result.addError(self, failure) | |
| 727 if failure.check(KeyboardInterrupt): | |
| 728 result.stop() | |
| 729 self._passed = False | |
| 730 | |
| 731 def deferTearDownClass(self, ignored, result): | |
| 732 d = self._run('tearDownClass', result) | |
| 733 d.addErrback(self._ebTearDownClass, result) | |
| 734 return d | |
| 735 | |
| 736 def _ebTearDownClass(self, error, result): | |
| 737 if error.check(KeyboardInterrupt): | |
| 738 result.stop() | |
| 739 result.addError(self, error) | |
| 740 | |
| 741 def _cleanUp(self, result): | |
| 742 try: | |
| 743 clean = util._Janitor(self, result).postCaseCleanup() | |
| 744 if not clean: | |
| 745 self._passed = False | |
| 746 except: | |
| 747 result.addError(self, failure.Failure()) | |
| 748 self._passed = False | |
| 749 for error in self._observer.getErrors(): | |
| 750 result.addError(self, error) | |
| 751 self._passed = False | |
| 752 self.flushLoggedErrors() | |
| 753 self._removeObserver() | |
| 754 if self._passed: | |
| 755 result.addSuccess(self) | |
| 756 | |
| 757 def _classCleanUp(self, result): | |
| 758 try: | |
| 759 util._Janitor(self, result).postClassCleanup() | |
| 760 except: | |
| 761 result.addError(self, failure.Failure()) | |
| 762 | |
| 763 def _makeReactorMethod(self, name): | |
| 764 """ | |
| 765 Create a method which wraps the reactor method C{name}. The new | |
| 766 method issues a deprecation warning and calls the original. | |
| 767 """ | |
| 768 def _(*a, **kw): | |
| 769 warnings.warn("reactor.%s cannot be used inside unit tests. " | |
| 770 "In the future, using %s will fail the test and may " | |
| 771 "crash or hang the test run." | |
| 772 % (name, name), | |
| 773 stacklevel=2, category=DeprecationWarning) | |
| 774 return self._reactorMethods[name](*a, **kw) | |
| 775 return _ | |
| 776 | |
| 777 def _deprecateReactor(self, reactor): | |
| 778 """ | |
| 779 Deprecate C{iterate}, C{crash} and C{stop} on C{reactor}. That is, | |
| 780 each method is wrapped in a function that issues a deprecation | |
| 781 warning, then calls the original. | |
| 782 | |
| 783 @param reactor: The Twisted reactor. | |
| 784 """ | |
| 785 self._reactorMethods = {} | |
| 786 for name in ['crash', 'iterate', 'stop']: | |
| 787 self._reactorMethods[name] = getattr(reactor, name) | |
| 788 setattr(reactor, name, self._makeReactorMethod(name)) | |
| 789 | |
| 790 def _undeprecateReactor(self, reactor): | |
| 791 """ | |
| 792 Restore the deprecated reactor methods. Undoes what | |
| 793 L{_deprecateReactor} did. | |
| 794 | |
| 795 @param reactor: The Twisted reactor. | |
| 796 """ | |
| 797 for name, method in self._reactorMethods.iteritems(): | |
| 798 setattr(reactor, name, method) | |
| 799 self._reactorMethods = {} | |
| 800 | |
| 801 def _installObserver(self): | |
| 802 self._observer = _logObserver | |
| 803 self._observer._add() | |
| 804 | |
| 805 def _removeObserver(self): | |
| 806 self._observer._remove() | |
| 807 | |
| 808 def flushLoggedErrors(self, *errorTypes): | |
| 809 """ | |
| 810 Remove stored errors received from the log. | |
| 811 | |
| 812 C{TestCase} stores each error logged during the run of the test and | |
| 813 reports them as errors during the cleanup phase (after C{tearDown}). | |
| 814 | |
| 815 @param *errorTypes: If unspecifed, flush all errors. Otherwise, only | |
| 816 flush errors that match the given types. | |
| 817 | |
| 818 @return: A list of failures that have been removed. | |
| 819 """ | |
| 820 return self._observer.flushErrors(*errorTypes) | |
| 821 | |
| 822 | |
| 823 def addCleanup(self, f, *args, **kwargs): | |
| 824 """ | |
| 825 Add the given function to a list of functions to be called after the | |
| 826 test has run, but before C{tearDown}. | |
| 827 | |
| 828 Functions will be run in reverse order of being added. This helps | |
| 829 ensure that tear down complements set up. | |
| 830 | |
| 831 The function C{f} may return a Deferred. If so, C{TestCase} will wait | |
| 832 until the Deferred has fired before proceeding to the next function. | |
| 833 """ | |
| 834 self._cleanups.append((f, args, kwargs)) | |
| 835 | |
| 836 | |
| 837 def _captureDeprecationWarnings(self, f, *args, **kwargs): | |
| 838 """ | |
| 839 Call C{f} and capture all deprecation warnings. | |
| 840 """ | |
| 841 warnings = [] | |
| 842 def accumulateDeprecations(message, category, stacklevel): | |
| 843 self.assertEqual(DeprecationWarning, category) | |
| 844 self.assertEqual(stacklevel, 2) | |
| 845 warnings.append(message) | |
| 846 | |
| 847 originalMethod = deprecate.getWarningMethod() | |
| 848 deprecate.setWarningMethod(accumulateDeprecations) | |
| 849 try: | |
| 850 result = f(*args, **kwargs) | |
| 851 finally: | |
| 852 deprecate.setWarningMethod(originalMethod) | |
| 853 return (warnings, result) | |
| 854 | |
| 855 | |
| 856 def callDeprecated(self, version, f, *args, **kwargs): | |
| 857 """ | |
| 858 Call a function that was deprecated at a specific version. | |
| 859 | |
| 860 @param version: The version that the function was deprecated in. | |
| 861 @param f: The deprecated function to call. | |
| 862 @return: Whatever the function returns. | |
| 863 """ | |
| 864 warnings, result = self._captureDeprecationWarnings( | |
| 865 f, *args, **kwargs) | |
| 866 | |
| 867 if len(warnings) == 0: | |
| 868 self.fail('%r is not deprecated.' % (f,)) | |
| 869 | |
| 870 observedWarning = warnings[0] | |
| 871 expectedWarning = getDeprecationWarningString(f, version) | |
| 872 self.assertEqual(expectedWarning, observedWarning) | |
| 873 | |
| 874 return result | |
| 875 | |
| 876 | |
| 877 def _runCleanups(self): | |
| 878 """ | |
| 879 Run the cleanups added with L{addCleanup} in order. | |
| 880 | |
| 881 @return: A C{Deferred} that fires when all cleanups are run. | |
| 882 """ | |
| 883 def _makeFunction(f, args, kwargs): | |
| 884 return lambda: f(*args, **kwargs) | |
| 885 callables = [] | |
| 886 while len(self._cleanups) > 0: | |
| 887 f, args, kwargs = self._cleanups.pop() | |
| 888 callables.append(_makeFunction(f, args, kwargs)) | |
| 889 return util._runSequentially(callables) | |
| 890 | |
| 891 | |
| 892 def patch(self, obj, attribute, value): | |
| 893 """ | |
| 894 Monkey patch an object for the duration of the test. | |
| 895 | |
| 896 The monkey patch will be reverted at the end of the test using the | |
| 897 L{addCleanup} mechanism. | |
| 898 | |
| 899 The L{MonkeyPatcher} is returned so that users can restore and | |
| 900 re-apply the monkey patch within their tests. | |
| 901 | |
| 902 @param obj: The object to monkey patch. | |
| 903 @param attribute: The name of the attribute to change. | |
| 904 @param value: The value to set the attribute to. | |
| 905 @return: A L{monkey.MonkeyPatcher} object. | |
| 906 """ | |
| 907 monkeyPatch = monkey.MonkeyPatcher((obj, attribute, value)) | |
| 908 monkeyPatch.patch() | |
| 909 self.addCleanup(monkeyPatch.restore) | |
| 910 return monkeyPatch | |
| 911 | |
| 912 | |
| 913 def runTest(self): | |
| 914 """ | |
| 915 If no C{methodName} argument is passed to the constructor, L{run} will | |
| 916 treat this method as the thing with the actual test inside. | |
| 917 """ | |
| 918 | |
| 919 | |
| 920 def run(self, result): | |
| 921 """ | |
| 922 Run the test case, storing the results in C{result}. | |
| 923 | |
| 924 First runs C{setUp} on self, then runs the test method (defined in the | |
| 925 constructor), then runs C{tearDown}. Any of these may return | |
| 926 L{Deferred}s. After they complete, does some reactor cleanup. | |
| 927 | |
| 928 @param result: A L{TestResult} object. | |
| 929 """ | |
| 930 log.msg("--> %s <--" % (self.id())) | |
| 931 from twisted.internet import reactor | |
| 932 new_result = itrial.IReporter(result, None) | |
| 933 if new_result is None: | |
| 934 result = PyUnitResultAdapter(result) | |
| 935 else: | |
| 936 result = new_result | |
| 937 self._timedOut = False | |
| 938 if self._shared and self not in self.__class__._instances: | |
| 939 self.__class__._instances.add(self) | |
| 940 result.startTest(self) | |
| 941 if self.getSkip(): # don't run test methods that are marked as .skip | |
| 942 result.addSkip(self, self.getSkip()) | |
| 943 result.stopTest(self) | |
| 944 return | |
| 945 self._installObserver() | |
| 946 self._passed = False | |
| 947 first = False | |
| 948 if self._shared: | |
| 949 first = self._isFirst() | |
| 950 self.__class__._instancesRun.add(self) | |
| 951 self._deprecateReactor(reactor) | |
| 952 try: | |
| 953 if first: | |
| 954 d = self.deferSetUpClass(result) | |
| 955 else: | |
| 956 d = self.deferSetUp(None, result) | |
| 957 try: | |
| 958 self._wait(d) | |
| 959 finally: | |
| 960 self._cleanUp(result) | |
| 961 result.stopTest(self) | |
| 962 if self._shared and self._isLast(): | |
| 963 self._initInstances() | |
| 964 self._classCleanUp(result) | |
| 965 if not self._shared: | |
| 966 self._classCleanUp(result) | |
| 967 finally: | |
| 968 self._undeprecateReactor(reactor) | |
| 969 | |
| 970 def _getReason(self, f): | |
| 971 if len(f.value.args) > 0: | |
| 972 reason = f.value.args[0] | |
| 973 else: | |
| 974 warnings.warn(("Do not raise unittest.SkipTest with no " | |
| 975 "arguments! Give a reason for skipping tests!"), | |
| 976 stacklevel=2) | |
| 977 reason = f | |
| 978 return reason | |
| 979 | |
| 980 def getSkip(self): | |
| 981 """ | |
| 982 Return the skip reason set on this test, if any is set. Checks on the | |
| 983 instance first, then the class, then the module, then packages. As | |
| 984 soon as it finds something with a C{skip} attribute, returns that. | |
| 985 Returns C{None} if it cannot find anything. See L{TestCase} docstring | |
| 986 for more details. | |
| 987 """ | |
| 988 return util.acquireAttribute(self._parents, 'skip', None) | |
| 989 | |
| 990 def getTodo(self): | |
| 991 """ | |
| 992 Return a L{Todo} object if the test is marked todo. Checks on the | |
| 993 instance first, then the class, then the module, then packages. As | |
| 994 soon as it finds something with a C{todo} attribute, returns that. | |
| 995 Returns C{None} if it cannot find anything. See L{TestCase} docstring | |
| 996 for more details. | |
| 997 """ | |
| 998 todo = util.acquireAttribute(self._parents, 'todo', None) | |
| 999 if todo is None: | |
| 1000 return None | |
| 1001 return makeTodo(todo) | |
| 1002 | |
| 1003 def getTimeout(self): | |
| 1004 """ | |
| 1005 Returns the timeout value set on this test. Checks on the instance | |
| 1006 first, then the class, then the module, then packages. As soon as it | |
| 1007 finds something with a C{timeout} attribute, returns that. Returns | |
| 1008 L{util.DEFAULT_TIMEOUT_DURATION} if it cannot find anything. See | |
| 1009 L{TestCase} docstring for more details. | |
| 1010 """ | |
| 1011 timeout = util.acquireAttribute(self._parents, 'timeout', | |
| 1012 util.DEFAULT_TIMEOUT_DURATION) | |
| 1013 try: | |
| 1014 return float(timeout) | |
| 1015 except (ValueError, TypeError): | |
| 1016 # XXX -- this is here because sometimes people will have methods | |
| 1017 # called 'timeout', or set timeout to 'orange', or something | |
| 1018 # Particularly, test_news.NewsTestCase and ReactorCoreTestCase | |
| 1019 # both do this. | |
| 1020 warnings.warn("'timeout' attribute needs to be a number.", | |
| 1021 category=DeprecationWarning) | |
| 1022 return util.DEFAULT_TIMEOUT_DURATION | |
| 1023 | |
| 1024 def getSuppress(self): | |
| 1025 """ | |
| 1026 Returns any warning suppressions set for this test. Checks on the | |
| 1027 instance first, then the class, then the module, then packages. As | |
| 1028 soon as it finds something with a C{suppress} attribute, returns that. | |
| 1029 Returns any empty list (i.e. suppress no warnings) if it cannot find | |
| 1030 anything. See L{TestCase} docstring for more details. | |
| 1031 """ | |
| 1032 return util.acquireAttribute(self._parents, 'suppress', []) | |
| 1033 | |
| 1034 | |
| 1035 def visit(self, visitor): | |
| 1036 """ | |
| 1037 Visit this test case. Call C{visitor} with C{self} as a parameter. | |
| 1038 | |
| 1039 Deprecated in Twisted 8.0. | |
| 1040 | |
| 1041 @param visitor: A callable which expects a single parameter: a test | |
| 1042 case. | |
| 1043 | |
| 1044 @return: None | |
| 1045 """ | |
| 1046 warnings.warn("Test visitors deprecated in Twisted 8.0", | |
| 1047 category=DeprecationWarning) | |
| 1048 visitor(self) | |
| 1049 | |
| 1050 | |
| 1051 def mktemp(self): | |
| 1052 """Returns a unique name that may be used as either a temporary | |
| 1053 directory or filename. | |
| 1054 | |
| 1055 @note: you must call os.mkdir on the value returned from this | |
| 1056 method if you wish to use it as a directory! | |
| 1057 """ | |
| 1058 MAX_FILENAME = 32 # some platforms limit lengths of filenames | |
| 1059 base = os.path.join(self.__class__.__module__[:MAX_FILENAME], | |
| 1060 self.__class__.__name__[:MAX_FILENAME], | |
| 1061 self._testMethodName[:MAX_FILENAME]) | |
| 1062 if not os.path.exists(base): | |
| 1063 os.makedirs(base) | |
| 1064 dirname = tempfile.mkdtemp('', '', base) | |
| 1065 return os.path.join(dirname, 'temp') | |
| 1066 | |
| 1067 def _wait(self, d, running=_wait_is_running): | |
| 1068 """Take a Deferred that only ever callbacks. Block until it happens. | |
| 1069 """ | |
| 1070 from twisted.internet import reactor | |
| 1071 if running: | |
| 1072 raise RuntimeError("_wait is not reentrant") | |
| 1073 | |
| 1074 results = [] | |
| 1075 def append(any): | |
| 1076 if results is not None: | |
| 1077 results.append(any) | |
| 1078 def crash(ign): | |
| 1079 if results is not None: | |
| 1080 reactor.crash() | |
| 1081 crash = utils.suppressWarnings( | |
| 1082 crash, util.suppress(message=r'reactor\.crash cannot be used.*', | |
| 1083 category=DeprecationWarning)) | |
| 1084 def stop(): | |
| 1085 reactor.crash() | |
| 1086 stop = utils.suppressWarnings( | |
| 1087 stop, util.suppress(message=r'reactor\.crash cannot be used.*', | |
| 1088 category=DeprecationWarning)) | |
| 1089 | |
| 1090 running.append(None) | |
| 1091 try: | |
| 1092 d.addBoth(append) | |
| 1093 if results: | |
| 1094 # d might have already been fired, in which case append is | |
| 1095 # called synchronously. Avoid any reactor stuff. | |
| 1096 return | |
| 1097 d.addBoth(crash) | |
| 1098 reactor.stop = stop | |
| 1099 try: | |
| 1100 reactor.run() | |
| 1101 finally: | |
| 1102 del reactor.stop | |
| 1103 | |
| 1104 # If the reactor was crashed elsewhere due to a timeout, hopefully | |
| 1105 # that crasher also reported an error. Just return. | |
| 1106 # _timedOut is most likely to be set when d has fired but hasn't | |
| 1107 # completed its callback chain (see self._run) | |
| 1108 if results or self._timedOut: #defined in run() and _run() | |
| 1109 return | |
| 1110 | |
| 1111 # If the timeout didn't happen, and we didn't get a result or | |
| 1112 # a failure, then the user probably aborted the test, so let's | |
| 1113 # just raise KeyboardInterrupt. | |
| 1114 | |
| 1115 # FIXME: imagine this: | |
| 1116 # web/test/test_webclient.py: | |
| 1117 # exc = self.assertRaises(error.Error, wait, method(url)) | |
| 1118 # | |
| 1119 # wait() will raise KeyboardInterrupt, and assertRaises will | |
| 1120 # swallow it. Therefore, wait() raising KeyboardInterrupt is | |
| 1121 # insufficient to stop trial. A suggested solution is to have | |
| 1122 # this code set a "stop trial" flag, or otherwise notify trial | |
| 1123 # that it should really try to stop as soon as possible. | |
| 1124 raise KeyboardInterrupt() | |
| 1125 finally: | |
| 1126 results = None | |
| 1127 running.pop() | |
| 1128 | |
| 1129 | |
| 1130 class UnsupportedTrialFeature(Exception): | |
| 1131 """A feature of twisted.trial was used that pyunit cannot support.""" | |
| 1132 | |
| 1133 | |
| 1134 class PyUnitResultAdapter(object): | |
| 1135 """ | |
| 1136 Wrap a C{TestResult} from the standard library's C{unittest} so that it | |
| 1137 supports the extended result types from Trial, and also supports | |
| 1138 L{twisted.python.failure.Failure}s being passed to L{addError} and | |
| 1139 L{addFailure}. | |
| 1140 """ | |
| 1141 | |
| 1142 def __init__(self, original): | |
| 1143 """ | |
| 1144 @param original: A C{TestResult} instance from C{unittest}. | |
| 1145 """ | |
| 1146 self.original = original | |
| 1147 | |
| 1148 def _exc_info(self, err): | |
| 1149 if isinstance(err, failure.Failure): | |
| 1150 # Unwrap the Failure into a exc_info tuple. | |
| 1151 err = (err.type, err.value, err.getTracebackObject()) | |
| 1152 return err | |
| 1153 | |
| 1154 def startTest(self, method): | |
| 1155 self.original.startTest(method) | |
| 1156 | |
| 1157 def stopTest(self, method): | |
| 1158 self.original.stopTest(method) | |
| 1159 | |
| 1160 def addFailure(self, test, fail): | |
| 1161 self.original.addFailure(test, self._exc_info(fail)) | |
| 1162 | |
| 1163 def addError(self, test, error): | |
| 1164 self.original.addError(test, self._exc_info(error)) | |
| 1165 | |
| 1166 def _unsupported(self, test, feature, info): | |
| 1167 self.original.addFailure( | |
| 1168 test, | |
| 1169 (UnsupportedTrialFeature, | |
| 1170 UnsupportedTrialFeature(feature, info), | |
| 1171 None)) | |
| 1172 | |
| 1173 def addSkip(self, test, reason): | |
| 1174 """ | |
| 1175 Report the skip as a failure. | |
| 1176 """ | |
| 1177 self._unsupported(test, 'skip', reason) | |
| 1178 | |
| 1179 def addUnexpectedSuccess(self, test, todo): | |
| 1180 """ | |
| 1181 Report the unexpected success as a failure. | |
| 1182 """ | |
| 1183 self._unsupported(test, 'unexpected success', todo) | |
| 1184 | |
| 1185 def addExpectedFailure(self, test, error): | |
| 1186 """ | |
| 1187 Report the expected failure (i.e. todo) as a failure. | |
| 1188 """ | |
| 1189 self._unsupported(test, 'expected failure', error) | |
| 1190 | |
| 1191 def addSuccess(self, test): | |
| 1192 self.original.addSuccess(test) | |
| 1193 | |
| 1194 def upDownError(self, method, error, warn, printStatus): | |
| 1195 pass | |
| 1196 | |
| 1197 | |
| 1198 | |
| 1199 def suiteVisit(suite, visitor): | |
| 1200 """ | |
| 1201 Visit each test in C{suite} with C{visitor}. | |
| 1202 | |
| 1203 Deprecated in Twisted 8.0. | |
| 1204 | |
| 1205 @param visitor: A callable which takes a single argument, the L{TestCase} | |
| 1206 instance to visit. | |
| 1207 @return: None | |
| 1208 """ | |
| 1209 warnings.warn("Test visitors deprecated in Twisted 8.0", | |
| 1210 category=DeprecationWarning) | |
| 1211 for case in suite._tests: | |
| 1212 visit = getattr(case, 'visit', None) | |
| 1213 if visit is not None: | |
| 1214 visit(visitor) | |
| 1215 elif isinstance(case, pyunit.TestCase): | |
| 1216 case = itrial.ITestCase(case) | |
| 1217 case.visit(visitor) | |
| 1218 elif isinstance(case, pyunit.TestSuite): | |
| 1219 suiteVisit(case, visitor) | |
| 1220 else: | |
| 1221 case.visit(visitor) | |
| 1222 | |
| 1223 | |
| 1224 | |
| 1225 class TestSuite(pyunit.TestSuite): | |
| 1226 """ | |
| 1227 Extend the standard library's C{TestSuite} with support for the visitor | |
| 1228 pattern and a consistently overrideable C{run} method. | |
| 1229 """ | |
| 1230 | |
| 1231 visit = suiteVisit | |
| 1232 | |
| 1233 def __call__(self, result): | |
| 1234 return self.run(result) | |
| 1235 | |
| 1236 | |
| 1237 def run(self, result): | |
| 1238 """ | |
| 1239 Call C{run} on every member of the suite. | |
| 1240 """ | |
| 1241 # we implement this because Python 2.3 unittest defines this code | |
| 1242 # in __call__, whereas 2.4 defines the code in run. | |
| 1243 for test in self._tests: | |
| 1244 if result.shouldStop: | |
| 1245 break | |
| 1246 test(result) | |
| 1247 return result | |
| 1248 | |
| 1249 | |
| 1250 | |
| 1251 class TestDecorator(components.proxyForInterface(itrial.ITestCase, | |
| 1252 "_originalTest")): | |
| 1253 """ | |
| 1254 Decorator for test cases. | |
| 1255 | |
| 1256 @param _originalTest: The wrapped instance of test. | |
| 1257 @type _originalTest: A provider of L{itrial.ITestCase} | |
| 1258 """ | |
| 1259 | |
| 1260 implements(itrial.ITestCase) | |
| 1261 | |
| 1262 | |
| 1263 def __call__(self, result): | |
| 1264 """ | |
| 1265 Run the unit test. | |
| 1266 | |
| 1267 @param result: A TestResult object. | |
| 1268 """ | |
| 1269 return self.run(result) | |
| 1270 | |
| 1271 | |
| 1272 def run(self, result): | |
| 1273 """ | |
| 1274 Run the unit test. | |
| 1275 | |
| 1276 @param result: A TestResult object. | |
| 1277 """ | |
| 1278 return self._originalTest.run( | |
| 1279 reporter._AdaptedReporter(result, self.__class__)) | |
| 1280 | |
| 1281 | |
| 1282 | |
| 1283 def _clearSuite(suite): | |
| 1284 """ | |
| 1285 Clear all tests from C{suite}. | |
| 1286 | |
| 1287 This messes with the internals of C{suite}. In particular, it assumes that | |
| 1288 the suite keeps all of its tests in a list in an instance variable called | |
| 1289 C{_tests}. | |
| 1290 """ | |
| 1291 suite._tests = [] | |
| 1292 | |
| 1293 | |
| 1294 def decorate(test, decorator): | |
| 1295 """ | |
| 1296 Decorate all test cases in C{test} with C{decorator}. | |
| 1297 | |
| 1298 C{test} can be a test case or a test suite. If it is a test suite, then the | |
| 1299 structure of the suite is preserved. | |
| 1300 | |
| 1301 L{decorate} tries to preserve the class of the test suites it finds, but | |
| 1302 assumes the presence of the C{_tests} attribute on the suite. | |
| 1303 | |
| 1304 @param test: The C{TestCase} or C{TestSuite} to decorate. | |
| 1305 | |
| 1306 @param decorator: A unary callable used to decorate C{TestCase}s. | |
| 1307 | |
| 1308 @return: A decorated C{TestCase} or a C{TestSuite} containing decorated | |
| 1309 C{TestCase}s. | |
| 1310 """ | |
| 1311 | |
| 1312 try: | |
| 1313 tests = iter(test) | |
| 1314 except TypeError: | |
| 1315 return decorator(test) | |
| 1316 | |
| 1317 # At this point, we know that 'test' is a test suite. | |
| 1318 _clearSuite(test) | |
| 1319 | |
| 1320 for case in tests: | |
| 1321 test.addTest(decorate(case, decorator)) | |
| 1322 return test | |
| 1323 | |
| 1324 | |
| 1325 | |
| 1326 class _PyUnitTestCaseAdapter(TestDecorator): | |
| 1327 """ | |
| 1328 Adapt from pyunit.TestCase to ITestCase. | |
| 1329 """ | |
| 1330 | |
| 1331 | |
| 1332 def visit(self, visitor): | |
| 1333 """ | |
| 1334 Deprecated in Twisted 8.0. | |
| 1335 """ | |
| 1336 warnings.warn("Test visitors deprecated in Twisted 8.0", | |
| 1337 category=DeprecationWarning) | |
| 1338 visitor(self) | |
| 1339 | |
| 1340 | |
| 1341 | |
| 1342 class _BrokenIDTestCaseAdapter(_PyUnitTestCaseAdapter): | |
| 1343 """ | |
| 1344 Adapter for pyunit-style C{TestCase} subclasses that have undesirable id() | |
| 1345 methods. That is L{pyunit.FunctionTestCase} and L{pyunit.DocTestCase}. | |
| 1346 """ | |
| 1347 | |
| 1348 def id(self): | |
| 1349 """ | |
| 1350 Return the fully-qualified Python name of the doctest. | |
| 1351 """ | |
| 1352 testID = self._originalTest.shortDescription() | |
| 1353 if testID is not None: | |
| 1354 return testID | |
| 1355 return self._originalTest.id() | |
| 1356 | |
| 1357 | |
| 1358 | |
| 1359 class _ForceGarbageCollectionDecorator(TestDecorator): | |
| 1360 """ | |
| 1361 Forces garbage collection to be run before and after the test. Any errors | |
| 1362 logged during the post-test collection are added to the test result as | |
| 1363 errors. | |
| 1364 """ | |
| 1365 | |
| 1366 def run(self, result): | |
| 1367 gc.collect() | |
| 1368 TestDecorator.run(self, result) | |
| 1369 _logObserver._add() | |
| 1370 gc.collect() | |
| 1371 for error in _logObserver.getErrors(): | |
| 1372 result.addError(self, error) | |
| 1373 _logObserver.flushErrors() | |
| 1374 _logObserver._remove() | |
| 1375 | |
| 1376 | |
| 1377 components.registerAdapter( | |
| 1378 _PyUnitTestCaseAdapter, pyunit.TestCase, itrial.ITestCase) | |
| 1379 | |
| 1380 | |
| 1381 components.registerAdapter( | |
| 1382 _BrokenIDTestCaseAdapter, pyunit.FunctionTestCase, itrial.ITestCase) | |
| 1383 | |
| 1384 | |
| 1385 _docTestCase = getattr(doctest, 'DocTestCase', None) | |
| 1386 if _docTestCase: | |
| 1387 components.registerAdapter( | |
| 1388 _BrokenIDTestCaseAdapter, _docTestCase, itrial.ITestCase) | |
| 1389 | |
| 1390 | |
| 1391 def _iterateTests(testSuiteOrCase): | |
| 1392 """ | |
| 1393 Iterate through all of the test cases in C{testSuiteOrCase}. | |
| 1394 """ | |
| 1395 try: | |
| 1396 suite = iter(testSuiteOrCase) | |
| 1397 except TypeError: | |
| 1398 yield testSuiteOrCase | |
| 1399 else: | |
| 1400 for test in suite: | |
| 1401 for subtest in _iterateTests(test): | |
| 1402 yield subtest | |
| 1403 | |
| 1404 | |
| 1405 | |
| 1406 # Support for Python 2.3 | |
| 1407 try: | |
| 1408 iter(pyunit.TestSuite()) | |
| 1409 except TypeError: | |
| 1410 # Python 2.3's TestSuite doesn't support iteration. Let's monkey patch it! | |
| 1411 def __iter__(self): | |
| 1412 return iter(self._tests) | |
| 1413 pyunit.TestSuite.__iter__ = __iter__ | |
| 1414 | |
| 1415 | |
| 1416 | |
| 1417 class _SubTestCase(TestCase): | |
| 1418 def __init__(self): | |
| 1419 TestCase.__init__(self, 'run') | |
| 1420 | |
| 1421 _inst = _SubTestCase() | |
| 1422 | |
| 1423 def _deprecate(name): | |
| 1424 """ | |
| 1425 Internal method used to deprecate top-level assertions. Do not use this. | |
| 1426 """ | |
| 1427 def _(*args, **kwargs): | |
| 1428 warnings.warn("unittest.%s is deprecated. Instead use the %r " | |
| 1429 "method on unittest.TestCase" % (name, name), | |
| 1430 stacklevel=2, category=DeprecationWarning) | |
| 1431 return getattr(_inst, name)(*args, **kwargs) | |
| 1432 return _ | |
| 1433 | |
| 1434 | |
| 1435 _assertions = ['fail', 'failUnlessEqual', 'failIfEqual', 'failIfEquals', | |
| 1436 'failUnless', 'failUnlessIdentical', 'failUnlessIn', | |
| 1437 'failIfIdentical', 'failIfIn', 'failIf', | |
| 1438 'failUnlessAlmostEqual', 'failIfAlmostEqual', | |
| 1439 'failUnlessRaises', 'assertApproximates', | |
| 1440 'assertFailure', 'failUnlessSubstring', 'failIfSubstring', | |
| 1441 'assertAlmostEqual', 'assertAlmostEquals', | |
| 1442 'assertNotAlmostEqual', 'assertNotAlmostEquals', 'assertEqual', | |
| 1443 'assertEquals', 'assertNotEqual', 'assertNotEquals', | |
| 1444 'assertRaises', 'assert_', 'assertIdentical', | |
| 1445 'assertNotIdentical', 'assertIn', 'assertNotIn', | |
| 1446 'failUnlessFailure', 'assertSubstring', 'assertNotSubstring'] | |
| 1447 | |
| 1448 | |
| 1449 for methodName in _assertions: | |
| 1450 globals()[methodName] = _deprecate(methodName) | |
| 1451 | |
| 1452 | |
| 1453 __all__ = ['TestCase', 'wait', 'FailTest', 'SkipTest'] | |
| 1454 | |
| OLD | NEW |