| Benjamin Peterson | bed7d04 | 2009-07-19 21:01:52 +0000 | [diff] [blame] | 1 | """Test case implementation""" | 
 | 2 |  | 
 | 3 | import sys | 
 | 4 | import functools | 
 | 5 | import difflib | 
 | 6 | import pprint | 
 | 7 | import re | 
 | 8 | import warnings | 
 | 9 |  | 
 | 10 | from . import result, util | 
 | 11 |  | 
 | 12 |  | 
 | 13 | class SkipTest(Exception): | 
 | 14 |     """ | 
 | 15 |     Raise this exception in a test to skip it. | 
 | 16 |  | 
 | 17 |     Usually you can use TestResult.skip() or one of the skipping decorators | 
 | 18 |     instead of raising this directly. | 
 | 19 |     """ | 
 | 20 |     pass | 
 | 21 |  | 
 | 22 | class _ExpectedFailure(Exception): | 
 | 23 |     """ | 
 | 24 |     Raise this when a test is expected to fail. | 
 | 25 |  | 
 | 26 |     This is an implementation detail. | 
 | 27 |     """ | 
 | 28 |  | 
 | 29 |     def __init__(self, exc_info): | 
 | 30 |         super(_ExpectedFailure, self).__init__() | 
 | 31 |         self.exc_info = exc_info | 
 | 32 |  | 
 | 33 | class _UnexpectedSuccess(Exception): | 
 | 34 |     """ | 
 | 35 |     The test was supposed to fail, but it didn't! | 
 | 36 |     """ | 
 | 37 |     pass | 
 | 38 |  | 
 | 39 | def _id(obj): | 
 | 40 |     return obj | 
 | 41 |  | 
 | 42 | def skip(reason): | 
 | 43 |     """ | 
 | 44 |     Unconditionally skip a test. | 
 | 45 |     """ | 
 | 46 |     def decorator(test_item): | 
 | 47 |         if isinstance(test_item, type) and issubclass(test_item, TestCase): | 
 | 48 |             test_item.__unittest_skip__ = True | 
 | 49 |             test_item.__unittest_skip_why__ = reason | 
 | 50 |             return test_item | 
 | 51 |         @functools.wraps(test_item) | 
 | 52 |         def skip_wrapper(*args, **kwargs): | 
 | 53 |             raise SkipTest(reason) | 
 | 54 |         return skip_wrapper | 
 | 55 |     return decorator | 
 | 56 |  | 
 | 57 | def skipIf(condition, reason): | 
 | 58 |     """ | 
 | 59 |     Skip a test if the condition is true. | 
 | 60 |     """ | 
 | 61 |     if condition: | 
 | 62 |         return skip(reason) | 
 | 63 |     return _id | 
 | 64 |  | 
 | 65 | def skipUnless(condition, reason): | 
 | 66 |     """ | 
 | 67 |     Skip a test unless the condition is true. | 
 | 68 |     """ | 
 | 69 |     if not condition: | 
 | 70 |         return skip(reason) | 
 | 71 |     return _id | 
 | 72 |  | 
 | 73 |  | 
 | 74 | def expectedFailure(func): | 
 | 75 |     @functools.wraps(func) | 
 | 76 |     def wrapper(*args, **kwargs): | 
 | 77 |         try: | 
 | 78 |             func(*args, **kwargs) | 
 | 79 |         except Exception: | 
 | 80 |             raise _ExpectedFailure(sys.exc_info()) | 
 | 81 |         raise _UnexpectedSuccess | 
 | 82 |     return wrapper | 
 | 83 |  | 
 | 84 |  | 
 | 85 | class _AssertRaisesContext(object): | 
 | 86 |     """A context manager used to implement TestCase.assertRaises* methods.""" | 
 | 87 |  | 
 | 88 |     def __init__(self, expected, test_case, callable_obj=None, | 
 | 89 |                   expected_regexp=None): | 
 | 90 |         self.expected = expected | 
 | 91 |         self.failureException = test_case.failureException | 
 | 92 |         if callable_obj is not None: | 
 | 93 |             try: | 
 | 94 |                 self.obj_name = callable_obj.__name__ | 
 | 95 |             except AttributeError: | 
 | 96 |                 self.obj_name = str(callable_obj) | 
 | 97 |         else: | 
 | 98 |             self.obj_name = None | 
 | 99 |         self.expected_regex = expected_regexp | 
 | 100 |  | 
 | 101 |     def __enter__(self): | 
 | 102 |         pass | 
 | 103 |  | 
 | 104 |     def __exit__(self, exc_type, exc_value, tb): | 
 | 105 |         if exc_type is None: | 
 | 106 |             try: | 
 | 107 |                 exc_name = self.expected.__name__ | 
 | 108 |             except AttributeError: | 
 | 109 |                 exc_name = str(self.expected) | 
 | 110 |             if self.obj_name: | 
 | 111 |                 raise self.failureException("{0} not raised by {1}" | 
 | 112 |                     .format(exc_name, self.obj_name)) | 
 | 113 |             else: | 
 | 114 |                 raise self.failureException("{0} not raised" | 
 | 115 |                     .format(exc_name)) | 
 | 116 |         if not issubclass(exc_type, self.expected): | 
 | 117 |             # let unexpected exceptions pass through | 
 | 118 |             return False | 
| Kristján Valur Jónsson | 92a653a | 2009-11-13 16:10:13 +0000 | [diff] [blame^] | 119 |         #store exception, without traceback, for later retrieval | 
 | 120 |         self.exc_value = exc_value | 
 | 121 |         self.exc_value.with_traceback(None) | 
| Benjamin Peterson | bed7d04 | 2009-07-19 21:01:52 +0000 | [diff] [blame] | 122 |         if self.expected_regex is None: | 
 | 123 |             return True | 
 | 124 |  | 
 | 125 |         expected_regexp = self.expected_regex | 
 | 126 |         if isinstance(expected_regexp, (bytes, str)): | 
 | 127 |             expected_regexp = re.compile(expected_regexp) | 
 | 128 |         if not expected_regexp.search(str(exc_value)): | 
 | 129 |             raise self.failureException('"%s" does not match "%s"' % | 
 | 130 |                      (expected_regexp.pattern, str(exc_value))) | 
 | 131 |         return True | 
 | 132 |  | 
 | 133 |  | 
 | 134 | class _AssertWrapper(object): | 
 | 135 |     """Wrap entries in the _type_equality_funcs registry to make them deep | 
 | 136 |     copyable.""" | 
 | 137 |  | 
 | 138 |     def __init__(self, function): | 
 | 139 |         self.function = function | 
 | 140 |  | 
 | 141 |     def __deepcopy__(self, memo): | 
 | 142 |         memo[id(self)] = self | 
 | 143 |  | 
 | 144 |  | 
 | 145 | class TestCase(object): | 
 | 146 |     """A class whose instances are single test cases. | 
 | 147 |  | 
 | 148 |     By default, the test code itself should be placed in a method named | 
 | 149 |     'runTest'. | 
 | 150 |  | 
 | 151 |     If the fixture may be used for many test cases, create as | 
 | 152 |     many test methods as are needed. When instantiating such a TestCase | 
 | 153 |     subclass, specify in the constructor arguments the name of the test method | 
 | 154 |     that the instance is to execute. | 
 | 155 |  | 
 | 156 |     Test authors should subclass TestCase for their own tests. Construction | 
 | 157 |     and deconstruction of the test's environment ('fixture') can be | 
 | 158 |     implemented by overriding the 'setUp' and 'tearDown' methods respectively. | 
 | 159 |  | 
 | 160 |     If it is necessary to override the __init__ method, the base class | 
 | 161 |     __init__ method must always be called. It is important that subclasses | 
 | 162 |     should not change the signature of their __init__ method, since instances | 
 | 163 |     of the classes are instantiated automatically by parts of the framework | 
 | 164 |     in order to be run. | 
 | 165 |     """ | 
 | 166 |  | 
 | 167 |     # This attribute determines which exception will be raised when | 
 | 168 |     # the instance's assertion methods fail; test methods raising this | 
 | 169 |     # exception will be deemed to have 'failed' rather than 'errored' | 
 | 170 |  | 
 | 171 |     failureException = AssertionError | 
 | 172 |  | 
 | 173 |     # This attribute determines whether long messages (including repr of | 
 | 174 |     # objects used in assert methods) will be printed on failure in *addition* | 
 | 175 |     # to any explicit message passed. | 
 | 176 |  | 
 | 177 |     longMessage = False | 
 | 178 |  | 
 | 179 |  | 
 | 180 |     def __init__(self, methodName='runTest'): | 
 | 181 |         """Create an instance of the class that will use the named test | 
 | 182 |            method when executed. Raises a ValueError if the instance does | 
 | 183 |            not have a method with the specified name. | 
 | 184 |         """ | 
 | 185 |         self._testMethodName = methodName | 
 | 186 |         self._resultForDoCleanups = None | 
 | 187 |         try: | 
 | 188 |             testMethod = getattr(self, methodName) | 
 | 189 |         except AttributeError: | 
 | 190 |             raise ValueError("no such test method in %s: %s" % \ | 
 | 191 |                   (self.__class__, methodName)) | 
 | 192 |         self._testMethodDoc = testMethod.__doc__ | 
 | 193 |         self._cleanups = [] | 
 | 194 |  | 
 | 195 |         # Map types to custom assertEqual functions that will compare | 
 | 196 |         # instances of said type in more detail to generate a more useful | 
 | 197 |         # error message. | 
 | 198 |         self._type_equality_funcs = {} | 
 | 199 |         self.addTypeEqualityFunc(dict, self.assertDictEqual) | 
 | 200 |         self.addTypeEqualityFunc(list, self.assertListEqual) | 
 | 201 |         self.addTypeEqualityFunc(tuple, self.assertTupleEqual) | 
 | 202 |         self.addTypeEqualityFunc(set, self.assertSetEqual) | 
 | 203 |         self.addTypeEqualityFunc(frozenset, self.assertSetEqual) | 
 | 204 |  | 
 | 205 |     def addTypeEqualityFunc(self, typeobj, function): | 
 | 206 |         """Add a type specific assertEqual style function to compare a type. | 
 | 207 |  | 
 | 208 |         This method is for use by TestCase subclasses that need to register | 
 | 209 |         their own type equality functions to provide nicer error messages. | 
 | 210 |  | 
 | 211 |         Args: | 
 | 212 |             typeobj: The data type to call this function on when both values | 
 | 213 |                     are of the same type in assertEqual(). | 
 | 214 |             function: The callable taking two arguments and an optional | 
 | 215 |                     msg= argument that raises self.failureException with a | 
 | 216 |                     useful error message when the two arguments are not equal. | 
 | 217 |         """ | 
 | 218 |         self._type_equality_funcs[typeobj] = _AssertWrapper(function) | 
 | 219 |  | 
 | 220 |     def addCleanup(self, function, *args, **kwargs): | 
 | 221 |         """Add a function, with arguments, to be called when the test is | 
 | 222 |         completed. Functions added are called on a LIFO basis and are | 
 | 223 |         called after tearDown on test failure or success. | 
 | 224 |  | 
 | 225 |         Cleanup items are called even if setUp fails (unlike tearDown).""" | 
 | 226 |         self._cleanups.append((function, args, kwargs)) | 
 | 227 |  | 
 | 228 |     def setUp(self): | 
 | 229 |         "Hook method for setting up the test fixture before exercising it." | 
 | 230 |         pass | 
 | 231 |  | 
 | 232 |     def tearDown(self): | 
 | 233 |         "Hook method for deconstructing the test fixture after testing it." | 
 | 234 |         pass | 
 | 235 |  | 
 | 236 |     def countTestCases(self): | 
 | 237 |         return 1 | 
 | 238 |  | 
 | 239 |     def defaultTestResult(self): | 
 | 240 |         return result.TestResult() | 
 | 241 |  | 
 | 242 |     def shortDescription(self): | 
 | 243 |         """Returns both the test method name and first line of its docstring. | 
 | 244 |  | 
 | 245 |         If no docstring is given, only returns the method name. | 
 | 246 |  | 
 | 247 |         This method overrides unittest.TestCase.shortDescription(), which | 
 | 248 |         only returns the first line of the docstring, obscuring the name | 
 | 249 |         of the test upon failure. | 
 | 250 |         """ | 
 | 251 |         desc = str(self) | 
 | 252 |         doc_first_line = None | 
 | 253 |  | 
 | 254 |         if self._testMethodDoc: | 
 | 255 |             doc_first_line = self._testMethodDoc.split("\n")[0].strip() | 
 | 256 |         if doc_first_line: | 
 | 257 |             desc = '\n'.join((desc, doc_first_line)) | 
 | 258 |         return desc | 
 | 259 |  | 
 | 260 |     def id(self): | 
 | 261 |         return "%s.%s" % (util.strclass(self.__class__), self._testMethodName) | 
 | 262 |  | 
 | 263 |     def __eq__(self, other): | 
 | 264 |         if type(self) is not type(other): | 
 | 265 |             return NotImplemented | 
 | 266 |  | 
 | 267 |         return self._testMethodName == other._testMethodName | 
 | 268 |  | 
 | 269 |     def __ne__(self, other): | 
 | 270 |         return not self == other | 
 | 271 |  | 
 | 272 |     def __hash__(self): | 
 | 273 |         return hash((type(self), self._testMethodName)) | 
 | 274 |  | 
 | 275 |     def __str__(self): | 
 | 276 |         return "%s (%s)" % (self._testMethodName, util.strclass(self.__class__)) | 
 | 277 |  | 
 | 278 |     def __repr__(self): | 
 | 279 |         return "<%s testMethod=%s>" % \ | 
 | 280 |                (util.strclass(self.__class__), self._testMethodName) | 
 | 281 |  | 
 | 282 |     def run(self, result=None): | 
 | 283 |         orig_result = result | 
 | 284 |         if result is None: | 
 | 285 |             result = self.defaultTestResult() | 
 | 286 |             startTestRun = getattr(result, 'startTestRun', None) | 
 | 287 |             if startTestRun is not None: | 
 | 288 |                 startTestRun() | 
 | 289 |  | 
 | 290 |         self._resultForDoCleanups = result | 
 | 291 |         result.startTest(self) | 
 | 292 |         if getattr(self.__class__, "__unittest_skip__", False): | 
 | 293 |             # If the whole class was skipped. | 
 | 294 |             try: | 
 | 295 |                 result.addSkip(self, self.__class__.__unittest_skip_why__) | 
 | 296 |             finally: | 
 | 297 |                 result.stopTest(self) | 
 | 298 |             return | 
 | 299 |         testMethod = getattr(self, self._testMethodName) | 
 | 300 |         try: | 
 | 301 |             success = False | 
 | 302 |             try: | 
 | 303 |                 self.setUp() | 
 | 304 |             except SkipTest as e: | 
 | 305 |                 result.addSkip(self, str(e)) | 
 | 306 |             except Exception: | 
 | 307 |                 result.addError(self, sys.exc_info()) | 
 | 308 |             else: | 
 | 309 |                 try: | 
 | 310 |                     testMethod() | 
 | 311 |                 except self.failureException: | 
 | 312 |                     result.addFailure(self, sys.exc_info()) | 
 | 313 |                 except _ExpectedFailure as e: | 
 | 314 |                     result.addExpectedFailure(self, e.exc_info) | 
 | 315 |                 except _UnexpectedSuccess: | 
 | 316 |                     result.addUnexpectedSuccess(self) | 
 | 317 |                 except SkipTest as e: | 
 | 318 |                     result.addSkip(self, str(e)) | 
 | 319 |                 except Exception: | 
 | 320 |                     result.addError(self, sys.exc_info()) | 
 | 321 |                 else: | 
 | 322 |                     success = True | 
 | 323 |  | 
 | 324 |                 try: | 
 | 325 |                     self.tearDown() | 
 | 326 |                 except Exception: | 
 | 327 |                     result.addError(self, sys.exc_info()) | 
 | 328 |                     success = False | 
 | 329 |  | 
 | 330 |             cleanUpSuccess = self.doCleanups() | 
 | 331 |             success = success and cleanUpSuccess | 
 | 332 |             if success: | 
 | 333 |                 result.addSuccess(self) | 
 | 334 |         finally: | 
 | 335 |             result.stopTest(self) | 
 | 336 |             if orig_result is None: | 
 | 337 |                 stopTestRun = getattr(result, 'stopTestRun', None) | 
 | 338 |                 if stopTestRun is not None: | 
 | 339 |                     stopTestRun() | 
 | 340 |  | 
 | 341 |     def doCleanups(self): | 
 | 342 |         """Execute all cleanup functions. Normally called for you after | 
 | 343 |         tearDown.""" | 
 | 344 |         result = self._resultForDoCleanups | 
 | 345 |         ok = True | 
 | 346 |         while self._cleanups: | 
 | 347 |             function, args, kwargs = self._cleanups.pop(-1) | 
 | 348 |             try: | 
 | 349 |                 function(*args, **kwargs) | 
 | 350 |             except Exception: | 
 | 351 |                 ok = False | 
 | 352 |                 result.addError(self, sys.exc_info()) | 
 | 353 |         return ok | 
 | 354 |  | 
 | 355 |     def __call__(self, *args, **kwds): | 
 | 356 |         return self.run(*args, **kwds) | 
 | 357 |  | 
 | 358 |     def debug(self): | 
 | 359 |         """Run the test without collecting errors in a TestResult""" | 
 | 360 |         self.setUp() | 
 | 361 |         getattr(self, self._testMethodName)() | 
 | 362 |         self.tearDown() | 
 | 363 |  | 
 | 364 |     def skipTest(self, reason): | 
 | 365 |         """Skip this test.""" | 
 | 366 |         raise SkipTest(reason) | 
 | 367 |  | 
 | 368 |     def fail(self, msg=None): | 
 | 369 |         """Fail immediately, with the given message.""" | 
 | 370 |         raise self.failureException(msg) | 
 | 371 |  | 
 | 372 |     def assertFalse(self, expr, msg=None): | 
 | 373 |         "Fail the test if the expression is true." | 
 | 374 |         if expr: | 
 | 375 |             msg = self._formatMessage(msg, "%r is not False" % expr) | 
 | 376 |             raise self.failureException(msg) | 
 | 377 |  | 
 | 378 |     def assertTrue(self, expr, msg=None): | 
 | 379 |         """Fail the test unless the expression is true.""" | 
 | 380 |         if not expr: | 
 | 381 |             msg = self._formatMessage(msg, "%r is not True" % expr) | 
 | 382 |             raise self.failureException(msg) | 
 | 383 |  | 
 | 384 |     def _formatMessage(self, msg, standardMsg): | 
 | 385 |         """Honour the longMessage attribute when generating failure messages. | 
 | 386 |         If longMessage is False this means: | 
 | 387 |         * Use only an explicit message if it is provided | 
 | 388 |         * Otherwise use the standard message for the assert | 
 | 389 |  | 
 | 390 |         If longMessage is True: | 
 | 391 |         * Use the standard message | 
 | 392 |         * If an explicit message is provided, plus ' : ' and the explicit message | 
 | 393 |         """ | 
 | 394 |         if not self.longMessage: | 
 | 395 |             return msg or standardMsg | 
 | 396 |         if msg is None: | 
 | 397 |             return standardMsg | 
 | 398 |         return standardMsg + ' : ' + msg | 
 | 399 |  | 
 | 400 |  | 
 | 401 |     def assertRaises(self, excClass, callableObj=None, *args, **kwargs): | 
 | 402 |         """Fail unless an exception of class excClass is thrown | 
 | 403 |            by callableObj when invoked with arguments args and keyword | 
 | 404 |            arguments kwargs. If a different type of exception is | 
 | 405 |            thrown, it will not be caught, and the test case will be | 
 | 406 |            deemed to have suffered an error, exactly as for an | 
 | 407 |            unexpected exception. | 
 | 408 |  | 
 | 409 |            If called with callableObj omitted or None, will return a | 
 | 410 |            context object used like this:: | 
 | 411 |  | 
 | 412 |                 with self.assertRaises(some_error_class): | 
 | 413 |                     do_something() | 
 | 414 |         """ | 
 | 415 |         context = _AssertRaisesContext(excClass, self, callableObj) | 
 | 416 |         if callableObj is None: | 
 | 417 |             return context | 
 | 418 |         with context: | 
 | 419 |             callableObj(*args, **kwargs) | 
 | 420 |  | 
 | 421 |     def _getAssertEqualityFunc(self, first, second): | 
 | 422 |         """Get a detailed comparison function for the types of the two args. | 
 | 423 |  | 
 | 424 |         Returns: A callable accepting (first, second, msg=None) that will | 
 | 425 |         raise a failure exception if first != second with a useful human | 
 | 426 |         readable error message for those types. | 
 | 427 |         """ | 
 | 428 |         # | 
 | 429 |         # NOTE(gregory.p.smith): I considered isinstance(first, type(second)) | 
 | 430 |         # and vice versa.  I opted for the conservative approach in case | 
 | 431 |         # subclasses are not intended to be compared in detail to their super | 
 | 432 |         # class instances using a type equality func.  This means testing | 
 | 433 |         # subtypes won't automagically use the detailed comparison.  Callers | 
 | 434 |         # should use their type specific assertSpamEqual method to compare | 
 | 435 |         # subclasses if the detailed comparison is desired and appropriate. | 
 | 436 |         # See the discussion in http://bugs.python.org/issue2578. | 
 | 437 |         # | 
 | 438 |         if type(first) is type(second): | 
 | 439 |             asserter = self._type_equality_funcs.get(type(first)) | 
 | 440 |             if asserter is not None: | 
 | 441 |                 return asserter.function | 
 | 442 |  | 
 | 443 |         return self._baseAssertEqual | 
 | 444 |  | 
 | 445 |     def _baseAssertEqual(self, first, second, msg=None): | 
 | 446 |         """The default assertEqual implementation, not type specific.""" | 
 | 447 |         if not first == second: | 
 | 448 |             standardMsg = '%r != %r' % (first, second) | 
 | 449 |             msg = self._formatMessage(msg, standardMsg) | 
 | 450 |             raise self.failureException(msg) | 
 | 451 |  | 
 | 452 |     def assertEqual(self, first, second, msg=None): | 
 | 453 |         """Fail if the two objects are unequal as determined by the '==' | 
 | 454 |            operator. | 
 | 455 |         """ | 
 | 456 |         assertion_func = self._getAssertEqualityFunc(first, second) | 
 | 457 |         assertion_func(first, second, msg=msg) | 
 | 458 |  | 
 | 459 |     def assertNotEqual(self, first, second, msg=None): | 
 | 460 |         """Fail if the two objects are equal as determined by the '==' | 
 | 461 |            operator. | 
 | 462 |         """ | 
 | 463 |         if not first != second: | 
 | 464 |             msg = self._formatMessage(msg, '%r == %r' % (first, second)) | 
 | 465 |             raise self.failureException(msg) | 
 | 466 |  | 
 | 467 |     def assertAlmostEqual(self, first, second, *, places=7, msg=None): | 
 | 468 |         """Fail if the two objects are unequal as determined by their | 
 | 469 |            difference rounded to the given number of decimal places | 
 | 470 |            (default 7) and comparing to zero. | 
 | 471 |  | 
 | 472 |            Note that decimal places (from zero) are usually not the same | 
 | 473 |            as significant digits (measured from the most signficant digit). | 
| Benjamin Peterson | 4ac9ce4 | 2009-10-04 14:49:41 +0000 | [diff] [blame] | 474 |  | 
 | 475 |            If the two objects compare equal then they will automatically | 
 | 476 |            compare almost equal. | 
| Benjamin Peterson | bed7d04 | 2009-07-19 21:01:52 +0000 | [diff] [blame] | 477 |         """ | 
| Benjamin Peterson | 4ac9ce4 | 2009-10-04 14:49:41 +0000 | [diff] [blame] | 478 |         if first == second: | 
 | 479 |             # shortcut for ite | 
 | 480 |             return | 
| Benjamin Peterson | bed7d04 | 2009-07-19 21:01:52 +0000 | [diff] [blame] | 481 |         if round(abs(second-first), places) != 0: | 
 | 482 |             standardMsg = '%r != %r within %r places' % (first, second, places) | 
 | 483 |             msg = self._formatMessage(msg, standardMsg) | 
 | 484 |             raise self.failureException(msg) | 
 | 485 |  | 
 | 486 |     def assertNotAlmostEqual(self, first, second, *, places=7, msg=None): | 
 | 487 |         """Fail if the two objects are equal as determined by their | 
 | 488 |            difference rounded to the given number of decimal places | 
 | 489 |            (default 7) and comparing to zero. | 
 | 490 |  | 
 | 491 |            Note that decimal places (from zero) are usually not the same | 
 | 492 |            as significant digits (measured from the most signficant digit). | 
| Benjamin Peterson | 4ac9ce4 | 2009-10-04 14:49:41 +0000 | [diff] [blame] | 493 |  | 
 | 494 |            Objects that are equal automatically fail. | 
| Benjamin Peterson | bed7d04 | 2009-07-19 21:01:52 +0000 | [diff] [blame] | 495 |         """ | 
| Benjamin Peterson | 4ac9ce4 | 2009-10-04 14:49:41 +0000 | [diff] [blame] | 496 |         if (first == second) or round(abs(second-first), places) == 0: | 
| Benjamin Peterson | bed7d04 | 2009-07-19 21:01:52 +0000 | [diff] [blame] | 497 |             standardMsg = '%r == %r within %r places' % (first, second, places) | 
 | 498 |             msg = self._formatMessage(msg, standardMsg) | 
 | 499 |             raise self.failureException(msg) | 
 | 500 |  | 
 | 501 |     # Synonyms for assertion methods | 
 | 502 |  | 
 | 503 |     # The plurals are undocumented.  Keep them that way to discourage use. | 
 | 504 |     # Do not add more.  Do not remove. | 
 | 505 |     # Going through a deprecation cycle on these would annoy many people. | 
 | 506 |     assertEquals = assertEqual | 
 | 507 |     assertNotEquals = assertNotEqual | 
 | 508 |     assertAlmostEquals = assertAlmostEqual | 
 | 509 |     assertNotAlmostEquals = assertNotAlmostEqual | 
 | 510 |     assert_ = assertTrue | 
 | 511 |  | 
 | 512 |     # These fail* assertion method names are pending deprecation and will | 
 | 513 |     # be a DeprecationWarning in 3.2; http://bugs.python.org/issue2578 | 
 | 514 |     def _deprecate(original_func): | 
 | 515 |         def deprecated_func(*args, **kwargs): | 
 | 516 |             warnings.warn( | 
 | 517 |                 'Please use {0} instead.'.format(original_func.__name__), | 
 | 518 |                 DeprecationWarning, 2) | 
 | 519 |             return original_func(*args, **kwargs) | 
 | 520 |         return deprecated_func | 
 | 521 |  | 
 | 522 |     failUnlessEqual = _deprecate(assertEqual) | 
 | 523 |     failIfEqual = _deprecate(assertNotEqual) | 
 | 524 |     failUnlessAlmostEqual = _deprecate(assertAlmostEqual) | 
 | 525 |     failIfAlmostEqual = _deprecate(assertNotAlmostEqual) | 
 | 526 |     failUnless = _deprecate(assertTrue) | 
 | 527 |     failUnlessRaises = _deprecate(assertRaises) | 
 | 528 |     failIf = _deprecate(assertFalse) | 
 | 529 |  | 
 | 530 |     def assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None): | 
 | 531 |         """An equality assertion for ordered sequences (like lists and tuples). | 
 | 532 |  | 
 | 533 |         For the purposes of this function, a valid orderd sequence type is one | 
 | 534 |         which can be indexed, has a length, and has an equality operator. | 
 | 535 |  | 
 | 536 |         Args: | 
 | 537 |             seq1: The first sequence to compare. | 
 | 538 |             seq2: The second sequence to compare. | 
 | 539 |             seq_type: The expected datatype of the sequences, or None if no | 
 | 540 |                     datatype should be enforced. | 
 | 541 |             msg: Optional message to use on failure instead of a list of | 
 | 542 |                     differences. | 
 | 543 |         """ | 
 | 544 |         if seq_type != None: | 
 | 545 |             seq_type_name = seq_type.__name__ | 
 | 546 |             if not isinstance(seq1, seq_type): | 
 | 547 |                 raise self.failureException('First sequence is not a %s: %r' | 
 | 548 |                                             % (seq_type_name, seq1)) | 
 | 549 |             if not isinstance(seq2, seq_type): | 
 | 550 |                 raise self.failureException('Second sequence is not a %s: %r' | 
 | 551 |                                             % (seq_type_name, seq2)) | 
 | 552 |         else: | 
 | 553 |             seq_type_name = "sequence" | 
 | 554 |  | 
 | 555 |         differing = None | 
 | 556 |         try: | 
 | 557 |             len1 = len(seq1) | 
 | 558 |         except (TypeError, NotImplementedError): | 
 | 559 |             differing = 'First %s has no length.    Non-sequence?' % ( | 
 | 560 |                     seq_type_name) | 
 | 561 |  | 
 | 562 |         if differing is None: | 
 | 563 |             try: | 
 | 564 |                 len2 = len(seq2) | 
 | 565 |             except (TypeError, NotImplementedError): | 
 | 566 |                 differing = 'Second %s has no length.    Non-sequence?' % ( | 
 | 567 |                         seq_type_name) | 
 | 568 |  | 
 | 569 |         if differing is None: | 
 | 570 |             if seq1 == seq2: | 
 | 571 |                 return | 
 | 572 |  | 
 | 573 |             seq1_repr = repr(seq1) | 
 | 574 |             seq2_repr = repr(seq2) | 
 | 575 |             if len(seq1_repr) > 30: | 
 | 576 |                 seq1_repr = seq1_repr[:30] + '...' | 
 | 577 |             if len(seq2_repr) > 30: | 
 | 578 |                 seq2_repr = seq2_repr[:30] + '...' | 
 | 579 |             elements = (seq_type_name.capitalize(), seq1_repr, seq2_repr) | 
 | 580 |             differing = '%ss differ: %s != %s\n' % elements | 
 | 581 |  | 
 | 582 |             for i in range(min(len1, len2)): | 
 | 583 |                 try: | 
 | 584 |                     item1 = seq1[i] | 
 | 585 |                 except (TypeError, IndexError, NotImplementedError): | 
 | 586 |                     differing += ('\nUnable to index element %d of first %s\n' % | 
 | 587 |                                  (i, seq_type_name)) | 
 | 588 |                     break | 
 | 589 |  | 
 | 590 |                 try: | 
 | 591 |                     item2 = seq2[i] | 
 | 592 |                 except (TypeError, IndexError, NotImplementedError): | 
 | 593 |                     differing += ('\nUnable to index element %d of second %s\n' % | 
 | 594 |                                  (i, seq_type_name)) | 
 | 595 |                     break | 
 | 596 |  | 
 | 597 |                 if item1 != item2: | 
 | 598 |                     differing += ('\nFirst differing element %d:\n%s\n%s\n' % | 
 | 599 |                                  (i, item1, item2)) | 
 | 600 |                     break | 
 | 601 |             else: | 
 | 602 |                 if (len1 == len2 and seq_type is None and | 
 | 603 |                     type(seq1) != type(seq2)): | 
 | 604 |                     # The sequences are the same, but have differing types. | 
 | 605 |                     return | 
 | 606 |  | 
 | 607 |             if len1 > len2: | 
 | 608 |                 differing += ('\nFirst %s contains %d additional ' | 
 | 609 |                              'elements.\n' % (seq_type_name, len1 - len2)) | 
 | 610 |                 try: | 
 | 611 |                     differing += ('First extra element %d:\n%s\n' % | 
 | 612 |                                   (len2, seq1[len2])) | 
 | 613 |                 except (TypeError, IndexError, NotImplementedError): | 
 | 614 |                     differing += ('Unable to index element %d ' | 
 | 615 |                                   'of first %s\n' % (len2, seq_type_name)) | 
 | 616 |             elif len1 < len2: | 
 | 617 |                 differing += ('\nSecond %s contains %d additional ' | 
 | 618 |                              'elements.\n' % (seq_type_name, len2 - len1)) | 
 | 619 |                 try: | 
 | 620 |                     differing += ('First extra element %d:\n%s\n' % | 
 | 621 |                                   (len1, seq2[len1])) | 
 | 622 |                 except (TypeError, IndexError, NotImplementedError): | 
 | 623 |                     differing += ('Unable to index element %d ' | 
 | 624 |                                   'of second %s\n' % (len1, seq_type_name)) | 
| Benjamin Peterson | 6e8c757 | 2009-10-04 20:19:21 +0000 | [diff] [blame] | 625 |         standardMsg = differing + '\n' + '\n'.join( | 
 | 626 |             difflib.ndiff(pprint.pformat(seq1).splitlines(), | 
 | 627 |                           pprint.pformat(seq2).splitlines())) | 
| Benjamin Peterson | bed7d04 | 2009-07-19 21:01:52 +0000 | [diff] [blame] | 628 |         msg = self._formatMessage(msg, standardMsg) | 
 | 629 |         self.fail(msg) | 
 | 630 |  | 
 | 631 |     def assertListEqual(self, list1, list2, msg=None): | 
 | 632 |         """A list-specific equality assertion. | 
 | 633 |  | 
 | 634 |         Args: | 
 | 635 |             list1: The first list to compare. | 
 | 636 |             list2: The second list to compare. | 
 | 637 |             msg: Optional message to use on failure instead of a list of | 
 | 638 |                     differences. | 
 | 639 |  | 
 | 640 |         """ | 
 | 641 |         self.assertSequenceEqual(list1, list2, msg, seq_type=list) | 
 | 642 |  | 
 | 643 |     def assertTupleEqual(self, tuple1, tuple2, msg=None): | 
 | 644 |         """A tuple-specific equality assertion. | 
 | 645 |  | 
 | 646 |         Args: | 
 | 647 |             tuple1: The first tuple to compare. | 
 | 648 |             tuple2: The second tuple to compare. | 
 | 649 |             msg: Optional message to use on failure instead of a list of | 
 | 650 |                     differences. | 
 | 651 |         """ | 
 | 652 |         self.assertSequenceEqual(tuple1, tuple2, msg, seq_type=tuple) | 
 | 653 |  | 
 | 654 |     def assertSetEqual(self, set1, set2, msg=None): | 
 | 655 |         """A set-specific equality assertion. | 
 | 656 |  | 
 | 657 |         Args: | 
 | 658 |             set1: The first set to compare. | 
 | 659 |             set2: The second set to compare. | 
 | 660 |             msg: Optional message to use on failure instead of a list of | 
 | 661 |                     differences. | 
 | 662 |  | 
 | 663 |         For more general containership equality, assertSameElements will work | 
 | 664 |         with things other than sets.    This uses ducktyping to support | 
 | 665 |         different types of sets, and is optimized for sets specifically | 
 | 666 |         (parameters must support a difference method). | 
 | 667 |         """ | 
 | 668 |         try: | 
 | 669 |             difference1 = set1.difference(set2) | 
 | 670 |         except TypeError as e: | 
 | 671 |             self.fail('invalid type when attempting set difference: %s' % e) | 
 | 672 |         except AttributeError as e: | 
 | 673 |             self.fail('first argument does not support set difference: %s' % e) | 
 | 674 |  | 
 | 675 |         try: | 
 | 676 |             difference2 = set2.difference(set1) | 
 | 677 |         except TypeError as e: | 
 | 678 |             self.fail('invalid type when attempting set difference: %s' % e) | 
 | 679 |         except AttributeError as e: | 
 | 680 |             self.fail('second argument does not support set difference: %s' % e) | 
 | 681 |  | 
 | 682 |         if not (difference1 or difference2): | 
 | 683 |             return | 
 | 684 |  | 
 | 685 |         lines = [] | 
 | 686 |         if difference1: | 
 | 687 |             lines.append('Items in the first set but not the second:') | 
 | 688 |             for item in difference1: | 
 | 689 |                 lines.append(repr(item)) | 
 | 690 |         if difference2: | 
 | 691 |             lines.append('Items in the second set but not the first:') | 
 | 692 |             for item in difference2: | 
 | 693 |                 lines.append(repr(item)) | 
 | 694 |  | 
 | 695 |         standardMsg = '\n'.join(lines) | 
 | 696 |         self.fail(self._formatMessage(msg, standardMsg)) | 
 | 697 |  | 
 | 698 |     def assertIn(self, member, container, msg=None): | 
 | 699 |         """Just like self.assertTrue(a in b), but with a nicer default message.""" | 
 | 700 |         if member not in container: | 
 | 701 |             standardMsg = '%r not found in %r' % (member, container) | 
 | 702 |             self.fail(self._formatMessage(msg, standardMsg)) | 
 | 703 |  | 
 | 704 |     def assertNotIn(self, member, container, msg=None): | 
 | 705 |         """Just like self.assertTrue(a not in b), but with a nicer default message.""" | 
 | 706 |         if member in container: | 
 | 707 |             standardMsg = '%r unexpectedly found in %r' % (member, container) | 
 | 708 |             self.fail(self._formatMessage(msg, standardMsg)) | 
 | 709 |  | 
 | 710 |     def assertIs(self, expr1, expr2, msg=None): | 
 | 711 |         """Just like self.assertTrue(a is b), but with a nicer default message.""" | 
 | 712 |         if expr1 is not expr2: | 
 | 713 |             standardMsg = '%r is not %r' % (expr1, expr2) | 
 | 714 |             self.fail(self._formatMessage(msg, standardMsg)) | 
 | 715 |  | 
 | 716 |     def assertIsNot(self, expr1, expr2, msg=None): | 
 | 717 |         """Just like self.assertTrue(a is not b), but with a nicer default message.""" | 
 | 718 |         if expr1 is expr2: | 
 | 719 |             standardMsg = 'unexpectedly identical: %r' % (expr1,) | 
 | 720 |             self.fail(self._formatMessage(msg, standardMsg)) | 
 | 721 |  | 
 | 722 |     def assertDictEqual(self, d1, d2, msg=None): | 
 | 723 |         self.assert_(isinstance(d1, dict), 'First argument is not a dictionary') | 
 | 724 |         self.assert_(isinstance(d2, dict), 'Second argument is not a dictionary') | 
 | 725 |  | 
 | 726 |         if d1 != d2: | 
 | 727 |             standardMsg = ('\n' + '\n'.join(difflib.ndiff( | 
 | 728 |                            pprint.pformat(d1).splitlines(), | 
 | 729 |                            pprint.pformat(d2).splitlines()))) | 
 | 730 |             self.fail(self._formatMessage(msg, standardMsg)) | 
 | 731 |  | 
 | 732 |     def assertDictContainsSubset(self, expected, actual, msg=None): | 
 | 733 |         """Checks whether actual is a superset of expected.""" | 
 | 734 |         missing = [] | 
 | 735 |         mismatched = [] | 
 | 736 |         for key, value in expected.items(): | 
 | 737 |             if key not in actual: | 
 | 738 |                 missing.append(key) | 
 | 739 |             elif value != actual[key]: | 
| Benjamin Peterson | 6e8c757 | 2009-10-04 20:19:21 +0000 | [diff] [blame] | 740 |                 mismatched.append('%s, expected: %s, actual: %s' % | 
 | 741 |                                   (key, value, actual[key])) | 
| Benjamin Peterson | bed7d04 | 2009-07-19 21:01:52 +0000 | [diff] [blame] | 742 |  | 
 | 743 |         if not (missing or mismatched): | 
 | 744 |             return | 
 | 745 |  | 
 | 746 |         standardMsg = '' | 
 | 747 |         if missing: | 
 | 748 |             standardMsg = 'Missing: %r' % ','.join(missing) | 
 | 749 |         if mismatched: | 
 | 750 |             if standardMsg: | 
 | 751 |                 standardMsg += '; ' | 
 | 752 |             standardMsg += 'Mismatched values: %s' % ','.join(mismatched) | 
 | 753 |  | 
 | 754 |         self.fail(self._formatMessage(msg, standardMsg)) | 
 | 755 |  | 
 | 756 |     def assertSameElements(self, expected_seq, actual_seq, msg=None): | 
 | 757 |         """An unordered sequence specific comparison. | 
 | 758 |  | 
 | 759 |         Raises with an error message listing which elements of expected_seq | 
 | 760 |         are missing from actual_seq and vice versa if any. | 
 | 761 |         """ | 
 | 762 |         try: | 
 | 763 |             expected = set(expected_seq) | 
 | 764 |             actual = set(actual_seq) | 
 | 765 |             missing = list(expected.difference(actual)) | 
 | 766 |             unexpected = list(actual.difference(expected)) | 
 | 767 |             missing.sort() | 
 | 768 |             unexpected.sort() | 
 | 769 |         except TypeError: | 
 | 770 |             # Fall back to slower list-compare if any of the objects are | 
 | 771 |             # not hashable. | 
 | 772 |             expected = list(expected_seq) | 
 | 773 |             actual = list(actual_seq) | 
 | 774 |             try: | 
 | 775 |                 expected.sort() | 
 | 776 |                 actual.sort() | 
 | 777 |             except TypeError: | 
 | 778 |                 missing, unexpected = util.unorderable_list_difference(expected, | 
 | 779 |                                                                        actual) | 
 | 780 |             else: | 
 | 781 |                 missing, unexpected = util.sorted_list_difference(expected, | 
 | 782 |                                                                   actual) | 
 | 783 |         errors = [] | 
 | 784 |         if missing: | 
 | 785 |             errors.append('Expected, but missing:\n    %r' % missing) | 
 | 786 |         if unexpected: | 
 | 787 |             errors.append('Unexpected, but present:\n    %r' % unexpected) | 
 | 788 |         if errors: | 
 | 789 |             standardMsg = '\n'.join(errors) | 
 | 790 |             self.fail(self._formatMessage(msg, standardMsg)) | 
 | 791 |  | 
 | 792 |     def assertMultiLineEqual(self, first, second, msg=None): | 
 | 793 |         """Assert that two multi-line strings are equal.""" | 
 | 794 |         self.assert_(isinstance(first, str), ( | 
 | 795 |                 'First argument is not a string')) | 
 | 796 |         self.assert_(isinstance(second, str), ( | 
 | 797 |                 'Second argument is not a string')) | 
 | 798 |  | 
 | 799 |         if first != second: | 
| Benjamin Peterson | 6e8c757 | 2009-10-04 20:19:21 +0000 | [diff] [blame] | 800 |             standardMsg = '\n' + ''.join(difflib.ndiff(first.splitlines(True), | 
 | 801 |                                                        second.splitlines(True))) | 
| Benjamin Peterson | bed7d04 | 2009-07-19 21:01:52 +0000 | [diff] [blame] | 802 |             self.fail(self._formatMessage(msg, standardMsg)) | 
 | 803 |  | 
 | 804 |     def assertLess(self, a, b, msg=None): | 
 | 805 |         """Just like self.assertTrue(a < b), but with a nicer default message.""" | 
 | 806 |         if not a < b: | 
 | 807 |             standardMsg = '%r not less than %r' % (a, b) | 
 | 808 |             self.fail(self._formatMessage(msg, standardMsg)) | 
 | 809 |  | 
 | 810 |     def assertLessEqual(self, a, b, msg=None): | 
 | 811 |         """Just like self.assertTrue(a <= b), but with a nicer default message.""" | 
 | 812 |         if not a <= b: | 
 | 813 |             standardMsg = '%r not less than or equal to %r' % (a, b) | 
 | 814 |             self.fail(self._formatMessage(msg, standardMsg)) | 
 | 815 |  | 
 | 816 |     def assertGreater(self, a, b, msg=None): | 
 | 817 |         """Just like self.assertTrue(a > b), but with a nicer default message.""" | 
 | 818 |         if not a > b: | 
 | 819 |             standardMsg = '%r not greater than %r' % (a, b) | 
 | 820 |             self.fail(self._formatMessage(msg, standardMsg)) | 
 | 821 |  | 
 | 822 |     def assertGreaterEqual(self, a, b, msg=None): | 
 | 823 |         """Just like self.assertTrue(a >= b), but with a nicer default message.""" | 
 | 824 |         if not a >= b: | 
 | 825 |             standardMsg = '%r not greater than or equal to %r' % (a, b) | 
 | 826 |             self.fail(self._formatMessage(msg, standardMsg)) | 
 | 827 |  | 
 | 828 |     def assertIsNone(self, obj, msg=None): | 
 | 829 |         """Same as self.assertTrue(obj is None), with a nicer default message.""" | 
 | 830 |         if obj is not None: | 
 | 831 |             standardMsg = '%r is not None' % obj | 
 | 832 |             self.fail(self._formatMessage(msg, standardMsg)) | 
 | 833 |  | 
 | 834 |     def assertIsNotNone(self, obj, msg=None): | 
 | 835 |         """Included for symmetry with assertIsNone.""" | 
 | 836 |         if obj is None: | 
 | 837 |             standardMsg = 'unexpectedly None' | 
 | 838 |             self.fail(self._formatMessage(msg, standardMsg)) | 
 | 839 |  | 
| Benjamin Peterson | 6e8c757 | 2009-10-04 20:19:21 +0000 | [diff] [blame] | 840 |     def assertIsInstance(self, obj, cls, msg=None): | 
 | 841 |         """Same as self.assertTrue(isinstance(obj, cls)), with a nicer | 
 | 842 |         default message.""" | 
 | 843 |         if not isinstance(obj, cls): | 
 | 844 |             standardMsg = '%r is not an instance of %r' % (obj, cls) | 
 | 845 |             self.fail(self._formatMessage(msg, standardMsg)) | 
 | 846 |  | 
 | 847 |     def assertNotIsInstance(self, obj, cls, msg=None): | 
 | 848 |         """Included for symmetry with assertIsInstance.""" | 
 | 849 |         if isinstance(obj, cls): | 
 | 850 |             standardMsg = '%r is an instance of %r' % (obj, cls) | 
 | 851 |             self.fail(self._formatMessage(msg, standardMsg)) | 
 | 852 |  | 
| Benjamin Peterson | bed7d04 | 2009-07-19 21:01:52 +0000 | [diff] [blame] | 853 |     def assertRaisesRegexp(self, expected_exception, expected_regexp, | 
 | 854 |                            callable_obj=None, *args, **kwargs): | 
 | 855 |         """Asserts that the message in a raised exception matches a regexp. | 
 | 856 |  | 
 | 857 |         Args: | 
 | 858 |             expected_exception: Exception class expected to be raised. | 
 | 859 |             expected_regexp: Regexp (re pattern object or string) expected | 
 | 860 |                     to be found in error message. | 
 | 861 |             callable_obj: Function to be called. | 
 | 862 |             args: Extra args. | 
 | 863 |             kwargs: Extra kwargs. | 
 | 864 |         """ | 
 | 865 |         context = _AssertRaisesContext(expected_exception, self, callable_obj, | 
 | 866 |                                        expected_regexp) | 
 | 867 |         if callable_obj is None: | 
 | 868 |             return context | 
 | 869 |         with context: | 
 | 870 |             callable_obj(*args, **kwargs) | 
 | 871 |  | 
 | 872 |     def assertRegexpMatches(self, text, expected_regex, msg=None): | 
 | 873 |         if isinstance(expected_regex, (str, bytes)): | 
 | 874 |             expected_regex = re.compile(expected_regex) | 
 | 875 |         if not expected_regex.search(text): | 
 | 876 |             msg = msg or "Regexp didn't match" | 
 | 877 |             msg = '%s: %r not found in %r' % (msg, expected_regex.pattern, text) | 
 | 878 |             raise self.failureException(msg) | 
 | 879 |  | 
 | 880 |  | 
 | 881 | class FunctionTestCase(TestCase): | 
 | 882 |     """A test case that wraps a test function. | 
 | 883 |  | 
 | 884 |     This is useful for slipping pre-existing test functions into the | 
 | 885 |     unittest framework. Optionally, set-up and tidy-up functions can be | 
 | 886 |     supplied. As with TestCase, the tidy-up ('tearDown') function will | 
 | 887 |     always be called if the set-up ('setUp') function ran successfully. | 
 | 888 |     """ | 
 | 889 |  | 
 | 890 |     def __init__(self, testFunc, setUp=None, tearDown=None, description=None): | 
 | 891 |         super(FunctionTestCase, self).__init__() | 
 | 892 |         self._setUpFunc = setUp | 
 | 893 |         self._tearDownFunc = tearDown | 
 | 894 |         self._testFunc = testFunc | 
 | 895 |         self._description = description | 
 | 896 |  | 
 | 897 |     def setUp(self): | 
 | 898 |         if self._setUpFunc is not None: | 
 | 899 |             self._setUpFunc() | 
 | 900 |  | 
 | 901 |     def tearDown(self): | 
 | 902 |         if self._tearDownFunc is not None: | 
 | 903 |             self._tearDownFunc() | 
 | 904 |  | 
 | 905 |     def runTest(self): | 
 | 906 |         self._testFunc() | 
 | 907 |  | 
 | 908 |     def id(self): | 
 | 909 |         return self._testFunc.__name__ | 
 | 910 |  | 
 | 911 |     def __eq__(self, other): | 
 | 912 |         if not isinstance(other, self.__class__): | 
 | 913 |             return NotImplemented | 
 | 914 |  | 
 | 915 |         return self._setUpFunc == other._setUpFunc and \ | 
 | 916 |                self._tearDownFunc == other._tearDownFunc and \ | 
 | 917 |                self._testFunc == other._testFunc and \ | 
 | 918 |                self._description == other._description | 
 | 919 |  | 
 | 920 |     def __ne__(self, other): | 
 | 921 |         return not self == other | 
 | 922 |  | 
 | 923 |     def __hash__(self): | 
 | 924 |         return hash((type(self), self._setUpFunc, self._tearDownFunc, | 
 | 925 |                      self._testFunc, self._description)) | 
 | 926 |  | 
 | 927 |     def __str__(self): | 
 | 928 |         return "%s (%s)" % (util.strclass(self.__class__), | 
 | 929 |                             self._testFunc.__name__) | 
 | 930 |  | 
 | 931 |     def __repr__(self): | 
 | 932 |         return "<%s testFunc=%s>" % (util.strclass(self.__class__), | 
 | 933 |                                      self._testFunc) | 
 | 934 |  | 
 | 935 |     def shortDescription(self): | 
 | 936 |         if self._description is not None: | 
 | 937 |             return self._description | 
 | 938 |         doc = self._testFunc.__doc__ | 
 | 939 |         return doc and doc.split("\n")[0].strip() or None |