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