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