| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 ''' | 2 ''' |
| 3 Python unit testing framework, based on Erich Gamma's JUnit and Kent Beck's | 3 Python unit testing framework, based on Erich Gamma's JUnit and Kent Beck's |
| 4 Smalltalk testing framework. | 4 Smalltalk testing framework. |
| 5 | 5 |
| 6 This module contains the core framework classes that form the basis of | 6 This module contains the core framework classes that form the basis of |
| 7 specific test cases and suites (TestCase, TestSuite etc.), and also a | 7 specific test cases and suites (TestCase, TestSuite etc.), and also a |
| 8 text-based utility class for running the tests and reporting the results | 8 text-based utility class for running the tests and reporting the results |
| 9 (TextTestRunner). | 9 (TextTestRunner). |
| 10 | 10 |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 38 THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH | 38 THIS CODE, EVEN IF THE AUTHOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
| 39 DAMAGE. | 39 DAMAGE. |
| 40 | 40 |
| 41 THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT | 41 THE AUTHOR SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT |
| 42 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | 42 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A |
| 43 PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, | 43 PARTICULAR PURPOSE. THE CODE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, |
| 44 AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, | 44 AND THERE IS NO OBLIGATION WHATSOEVER TO PROVIDE MAINTENANCE, |
| 45 SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. | 45 SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
| 46 ''' | 46 ''' |
| 47 | 47 |
| 48 import difflib | |
| 49 import functools | |
| 50 import os | |
| 51 import pprint | |
| 52 import re | |
| 53 import sys | |
| 48 import time | 54 import time |
| 49 import sys | |
| 50 import traceback | 55 import traceback |
| 51 import os | |
| 52 import types | 56 import types |
| 53 import functools | |
| 54 | 57 |
| 55 ############################################################################## | 58 ############################################################################## |
| 56 # Exported classes and functions | 59 # Exported classes and functions |
| 57 ############################################################################## | 60 ############################################################################## |
| 58 __all__ = ['TestResult', 'TestCase', 'TestSuite', 'ClassTestSuite', | 61 __all__ = ['TestResult', 'TestCase', 'TestSuite', 'ClassTestSuite', |
| 59 'TextTestRunner', 'TestLoader', 'FunctionTestCase', 'main', | 62 'TextTestRunner', 'TestLoader', 'FunctionTestCase', 'main', |
| 60 'defaultTestLoader', 'SkipTest', 'skip', 'skipIf', 'skipUnless', | 63 'defaultTestLoader', 'SkipTest', 'skip', 'skipIf', 'skipUnless', |
| 61 'expectedFailure'] | 64 'expectedFailure'] |
| 62 | 65 |
| 63 # Expose obsolete functions for backwards compatibility | 66 # Expose obsolete functions for backwards compatibility |
| (...skipping 180 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 244 length += 1 | 247 length += 1 |
| 245 tb = tb.tb_next | 248 tb = tb.tb_next |
| 246 return length | 249 return length |
| 247 | 250 |
| 248 def __repr__(self): | 251 def __repr__(self): |
| 249 return "<%s run=%i errors=%i failures=%i>" % \ | 252 return "<%s run=%i errors=%i failures=%i>" % \ |
| 250 (_strclass(self.__class__), self.testsRun, len(self.errors), | 253 (_strclass(self.__class__), self.testsRun, len(self.errors), |
| 251 len(self.failures)) | 254 len(self.failures)) |
| 252 | 255 |
| 253 | 256 |
| 254 class AssertRaisesContext(object): | 257 class AssertRaisesContext(object): |
|
GvR
2009/03/31 15:01:06
While you're at it, can you add a docstring?
gregory.p.smith
2009/03/31 16:37:02
On 2009/03/31 15:01:06, GvR wrote:
> While you're
| |
| 255 | 258 |
| 256 def __init__(self, expected, test_case): | 259 def __init__(self, expected, test_case, expected_regexp=None): |
| 257 self.expected = expected | 260 self.expected = expected |
| 258 self.failureException = test_case.failureException | 261 self.failureException = test_case.failureException |
| 259 | 262 self.expected_regex = expected_regexp |
| 263 | |
| 260 def __enter__(self): | 264 def __enter__(self): |
| 261 pass | 265 pass |
| 262 | 266 |
| 263 def __exit__(self, exc_type, exc_value, traceback): | 267 def __exit__(self, exc_type, exc_value, traceback): |
| 264 if exc_type is None: | 268 if exc_type is None: |
| 265 try: | 269 try: |
| 266 exc_name = self.expected.__name__ | 270 exc_name = self.expected.__name__ |
| 267 except AttributeError: | 271 except AttributeError: |
| 268 exc_name = str(self.expected) | 272 exc_name = str(self.expected) |
| 269 raise self.failureException( | 273 raise self.failureException( |
| 270 "{0} not raised".format(exc_name)) | 274 "{0} not raised".format(exc_name)) |
| 271 if issubclass(exc_type, self.expected): | 275 if not issubclass(exc_type, self.expected): |
| 276 # let unexpexted exceptions pass through | |
| 277 return False | |
| 278 if self.expected_regex is None: | |
| 272 return True | 279 return True |
| 273 # Let unexpected exceptions skip through | 280 |
| 274 return False | 281 expected_regexp = self.expected_regex |
| 282 if isinstance(expected_regexp, basestring): | |
| 283 expected_regexp = re.compile(expected_regexp) | |
| 284 if not expected_regexp.search(str(exc_value)): | |
| 285 raise self.failureException('"%s" does not match "%s"' % | |
| 286 (expected_regexp.pattern, str(exc_value))) | |
| 287 return True | |
| 288 | |
| 275 | 289 |
| 276 | 290 |
| 277 class TestCase(object): | 291 class TestCase(object): |
| 278 """A class whose instances are single test cases. | 292 """A class whose instances are single test cases. |
| 279 | 293 |
| 280 By default, the test code itself should be placed in a method named | 294 By default, the test code itself should be placed in a method named |
| 281 'runTest'. | 295 'runTest'. |
| 282 | 296 |
| 283 If the fixture may be used for many test cases, create as | 297 If the fixture may be used for many test cases, create as |
| 284 many test methods as are needed. When instantiating such a TestCase | 298 many test methods as are needed. When instantiating such a TestCase |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 307 method when executed. Raises a ValueError if the instance does | 321 method when executed. Raises a ValueError if the instance does |
| 308 not have a method with the specified name. | 322 not have a method with the specified name. |
| 309 """ | 323 """ |
| 310 self._testMethodName = methodName | 324 self._testMethodName = methodName |
| 311 try: | 325 try: |
| 312 testMethod = getattr(self, methodName) | 326 testMethod = getattr(self, methodName) |
| 313 except AttributeError: | 327 except AttributeError: |
| 314 raise ValueError("no such test method in %s: %s" % \ | 328 raise ValueError("no such test method in %s: %s" % \ |
| 315 (self.__class__, methodName)) | 329 (self.__class__, methodName)) |
| 316 self._testMethodDoc = testMethod.__doc__ | 330 self._testMethodDoc = testMethod.__doc__ |
| 331 | |
| 332 # Map types to custom assertEquals functions that will compare | |
|
GvR
2009/03/31 15:01:06
assertEquals -> assertEqual
gregory.p.smith
2009/03/31 16:37:02
On 2009/03/31 15:01:06, GvR wrote:
> assertEquals
| |
| 333 # instances of said type in more detail to generate a more useful | |
| 334 # error message. | |
| 335 self.__type_equality_funcs = {} | |
| 336 self.addTypeEqualityFunc(dict, self.assertDictEquals) | |
| 337 self.addTypeEqualityFunc(list, self.assertListEquals) | |
| 338 self.addTypeEqualityFunc(tuple, self.assertTupleEquals) | |
| 339 self.addTypeEqualityFunc(set, self.assertSetEquals) | |
| 340 self.addTypeEqualityFunc(frozenset, self.assertSetEquals) | |
| 341 | |
| 342 def addTypeEqualityFunc(self, typeobj, function): | |
| 343 """Add a type specific assertEqual style function to compare a type. | |
| 344 | |
| 345 This method is for use by TestCase subclasses that need to register | |
| 346 their own type equality functions to provide nicer error messages. | |
| 347 | |
| 348 Args: | |
| 349 typeobj: The data type to call this function on when both values | |
| 350 are of the same type in assertEquals(). | |
| 351 function: The callable taking two arguments and an optional | |
| 352 msg= argument that raises self.failureException with a | |
| 353 useful error message when the two arguments are not equal. | |
| 354 """ | |
| 355 self.__type_equality_funcs[typeobj] = function | |
| 317 | 356 |
| 318 def setUp(self): | 357 def setUp(self): |
| 319 "Hook method for setting up the test fixture before exercising it." | 358 "Hook method for setting up the test fixture before exercising it." |
| 320 pass | 359 pass |
| 321 | 360 |
| 322 def tearDown(self): | 361 def tearDown(self): |
| 323 "Hook method for deconstructing the test fixture after testing it." | 362 "Hook method for deconstructing the test fixture after testing it." |
| 324 pass | 363 pass |
| 325 | 364 |
| 326 def countTestCases(self): | 365 def countTestCases(self): |
| 327 return 1 | 366 return 1 |
| 328 | 367 |
| 329 def defaultTestResult(self): | 368 def defaultTestResult(self): |
| 330 return TestResult() | 369 return TestResult() |
| 331 | 370 |
| 332 def shortDescription(self): | 371 def shortDescription(self): |
| 333 """Returns a one-line description of the test, or None if no | 372 """Returns both the test method name and first line of its docstring. |
| 334 description has been provided. | |
| 335 | 373 |
| 336 The default implementation of this method returns the first line of | 374 If no docstring is given, only returns the method name. |
| 337 the specified test method's docstring. | 375 |
| 376 This method overrides unittest.TestCase.shortDescription(), which | |
| 377 only returns the first line of the docstring, obscuring the name | |
| 378 of the test upon failure. | |
| 338 """ | 379 """ |
| 339 doc = self._testMethodDoc | 380 desc = str(self) |
| 340 return doc and doc.split("\n")[0].strip() or None | 381 doc_first_line = None |
| 382 | |
| 383 if self._testMethodDoc: | |
| 384 doc_first_line = self._testMethodDoc.split("\n")[0].strip() | |
| 385 if doc_first_line: | |
| 386 desc = '\n'.join((desc, doc_first_line)) | |
| 387 return desc | |
| 341 | 388 |
| 342 def id(self): | 389 def id(self): |
| 343 return "%s.%s" % (_strclass(self.__class__), self._testMethodName) | 390 return "%s.%s" % (_strclass(self.__class__), self._testMethodName) |
| 344 | 391 |
| 345 def __eq__(self, other): | 392 def __eq__(self, other): |
| 346 if type(self) is not type(other): | 393 if type(self) is not type(other): |
| 347 return NotImplemented | 394 return NotImplemented |
| 348 | 395 |
| 349 return self._testMethodName == other._testMethodName | 396 return self._testMethodName == other._testMethodName |
| 350 | 397 |
| (...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 441 context object used like this:: | 488 context object used like this:: |
| 442 | 489 |
| 443 with self.failUnlessRaises(some_error_class): | 490 with self.failUnlessRaises(some_error_class): |
| 444 do_something() | 491 do_something() |
| 445 """ | 492 """ |
| 446 context = AssertRaisesContext(excClass, self) | 493 context = AssertRaisesContext(excClass, self) |
| 447 if callableObj is None: | 494 if callableObj is None: |
| 448 return context | 495 return context |
| 449 with context: | 496 with context: |
| 450 callableObj(*args, **kwargs) | 497 callableObj(*args, **kwargs) |
| 498 | |
| 499 def _getAssertEqualityFunc(self, first, second): | |
| 500 """Get a detailed comparison function for the types of the two args. | |
| 501 | |
| 502 Returns: A callable accepting (first, second, msg=None) that will | |
| 503 raise a failure exception if first != second with a useful human | |
| 504 readable error message for those types. | |
| 505 """ | |
| 506 # | |
| 507 # NOTE(gregory.p.smith): I considered isinstance(first, type(second)) | |
| 508 # and vice versa. I opted for the conservative approach in case | |
| 509 # subclasses are not intended to be compared in detail to their super | |
| 510 # class instances using a type equality func. This means testing | |
| 511 # subtypes won't automagically use the detailed comparison. Callers | |
| 512 # should use their type specific assertSpamEquals method to compare | |
|
GvR
2009/03/31 15:01:06
assertSpamEqual
gregory.p.smith
2009/03/31 16:37:02
On 2009/03/31 15:01:06, GvR wrote:
> assertSpamEqu
| |
| 513 # subclasses if the detailed comparison is desired and appropriate. | |
| 514 # See the discussion in http://bugs.python.org/issue2578. | |
| 515 # | |
| 516 if type(first) is type(second): | |
| 517 return self.__type_equality_funcs.get(type(first), | |
| 518 self._baseAssertEquals) | |
| 519 return self._baseAssertEquals | |
| 520 | |
| 521 def _baseAssertEquals(self, first, second, msg=None): | |
|
GvR
2009/03/31 15:01:06
Mind dropping the trailing 's'?
gregory.p.smith
2009/03/31 16:37:02
On 2009/03/31 15:01:06, GvR wrote:
> Mind dropping
| |
| 522 """The default assertEquals implementation, not type specific.""" | |
| 523 if not first == second: | |
| 524 raise self.failureException(msg or '%r != %r' % (first, second)) | |
| 451 | 525 |
| 452 def failUnlessEqual(self, first, second, msg=None): | 526 def failUnlessEqual(self, first, second, msg=None): |
|
GvR
2009/03/31 15:01:06
We had talked about making the 'def' define the re
gregory.p.smith
2009/03/31 16:37:02
On 2009/03/31 15:01:06, GvR wrote:
> We had talked
| |
| 453 """Fail if the two objects are unequal as determined by the '==' | 527 """Fail if the two objects are unequal as determined by the '==' |
| 454 operator. | 528 operator. |
| 455 """ | 529 """ |
| 456 if not first == second: | 530 assertion_func = self._getAssertEqualityFunc(first, second) |
| 457 raise self.failureException(msg or '%r != %r' % (first, second)) | 531 assertion_func(first, second, msg=msg) |
| 458 | 532 |
| 459 def failIfEqual(self, first, second, msg=None): | 533 def failIfEqual(self, first, second, msg=None): |
| 460 """Fail if the two objects are equal as determined by the '==' | 534 """Fail if the two objects are equal as determined by the '==' |
| 461 operator. | 535 operator. |
| 462 """ | 536 """ |
| 463 if first == second: | 537 if first == second: |
| 464 raise self.failureException(msg or '%r == %r' % (first, second)) | 538 raise self.failureException(msg or '%r == %r' % (first, second)) |
| 465 | 539 |
| 466 def failUnlessAlmostEqual(self, first, second, places=7, msg=None): | 540 def failUnlessAlmostEqual(self, first, second, places=7, msg=None): |
| 467 """Fail if the two objects are unequal as determined by their | 541 """Fail if the two objects are unequal as determined by their |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 496 assertAlmostEqual = assertAlmostEquals = failUnlessAlmostEqual | 570 assertAlmostEqual = assertAlmostEquals = failUnlessAlmostEqual |
| 497 | 571 |
| 498 assertNotAlmostEqual = assertNotAlmostEquals = failIfAlmostEqual | 572 assertNotAlmostEqual = assertNotAlmostEquals = failIfAlmostEqual |
| 499 | 573 |
| 500 assertRaises = failUnlessRaises | 574 assertRaises = failUnlessRaises |
| 501 | 575 |
| 502 assert_ = assertTrue = failUnless | 576 assert_ = assertTrue = failUnless |
| 503 | 577 |
| 504 assertFalse = failIf | 578 assertFalse = failIf |
| 505 | 579 |
| 580 | |
| 581 def assertSequenceEquals(self, seq1, seq2, msg=None, seq_type=None): | |
|
GvR
2009/03/31 15:01:06
Drop the trailing 's' in the name?
gregory.p.smith
2009/03/31 16:37:02
On 2009/03/31 15:01:06, GvR wrote:
> Drop the trai
| |
| 582 """An equality assertion for ordered sequences (like lists and tuples). | |
| 583 | |
| 584 For the purposes of this function, a valid orderd sequence type is one | |
| 585 which can be indexed, has a length, and has an equality operator. | |
| 586 | |
| 587 Args: | |
| 588 seq1: The first sequence to compare. | |
| 589 seq2: The second sequence to compare. | |
| 590 seq_type: The expected datatype of the sequences, or None if no | |
| 591 datatype should be enforced. | |
| 592 msg: Optional message to use on failure instead of a list of | |
| 593 differences. | |
| 594 """ | |
| 595 if seq_type != None: | |
| 596 seq_type_name = seq_type.__name__ | |
| 597 if not isinstance(seq1, seq_type): | |
| 598 raise self.failureException('First sequence is not a %s: %r' | |
| 599 % (seq_type_name, seq1)) | |
| 600 if not isinstance(seq2, seq_type): | |
| 601 raise self.failureException('Second sequence is not a %s: %r' | |
| 602 % (seq_type_name, seq2)) | |
| 603 else: | |
| 604 seq_type_name = "sequence" | |
| 605 | |
| 606 differing = None | |
| 607 try: | |
| 608 len1 = len(seq1) | |
| 609 except (TypeError, NotImplementedError): | |
| 610 differing = 'First %s has no length. Non-sequence?' % ( | |
| 611 seq_type_name) | |
| 612 | |
| 613 if differing is None: | |
| 614 try: | |
| 615 len2 = len(seq2) | |
| 616 except (TypeError, NotImplementedError): | |
| 617 differing = 'Second %s has no length. Non-sequence?' % ( | |
| 618 seq_type_name) | |
| 619 | |
| 620 if differing is None: | |
| 621 if seq1 == seq2: | |
| 622 return | |
| 623 | |
| 624 for i in xrange(min(len1, len2)): | |
| 625 try: | |
| 626 item1 = seq1[i] | |
| 627 except (TypeError, IndexError, NotImplementedError): | |
| 628 differing = ('Unable to index element %d of first %s\n' % | |
| 629 (i, seq_type_name)) | |
| 630 break | |
| 631 | |
| 632 try: | |
| 633 item2 = seq2[i] | |
| 634 except (TypeError, IndexError, NotImplementedError): | |
| 635 differing = ('Unable to index element %d of second %s\n' % | |
| 636 (i, seq_type_name)) | |
| 637 break | |
| 638 | |
| 639 if item1 != item2: | |
| 640 differing = ('First differing element %d:\n%s\n%s\n' % | |
| 641 (i, item1, item2)) | |
| 642 break | |
| 643 else: | |
| 644 if (len1 == len2 and seq_type is None and | |
| 645 type(seq1) != type(seq2)): | |
| 646 # The sequences are the same, but have differing types. | |
| 647 return | |
| 648 # A catch-all message for handling arbitrary user-defined | |
| 649 # sequences. | |
| 650 differing = '%ss differ:\n' % seq_type_name.capitalize() | |
| 651 if len1 > len2: | |
| 652 differing = ('First %s contains %d additional ' | |
| 653 'elements.\n' % (seq_type_name, len1 - len2)) | |
| 654 try: | |
| 655 differing += ('First extra element %d:\n%s\n' % | |
| 656 (len2, seq1[len2])) | |
| 657 except (TypeError, IndexError, NotImplementedError): | |
| 658 differing += ('Unable to index element %d ' | |
| 659 'of first %s\n' % (len2, seq_type_name)) | |
| 660 elif len1 < len2: | |
| 661 differing = ('Second %s contains %d additional ' | |
| 662 'elements.\n' % (seq_type_name, len2 - len1)) | |
| 663 try: | |
| 664 differing += ('First extra element %d:\n%s\n' % | |
| 665 (len1, seq2[len1])) | |
| 666 except (TypeError, IndexError, NotImplementedError): | |
| 667 differing += ('Unable to index element %d ' | |
| 668 'of second %s\n' % (len1, seq_type_name)) | |
| 669 if not msg: | |
| 670 msg = '\n'.join(difflib.ndiff(pprint.pformat(seq1).splitlines(), | |
| 671 pprint.pformat(seq2).splitlines())) | |
| 672 self.fail(differing + msg) | |
| 673 | |
| 674 def assertListEquals(self, list1, list2, msg=None): | |
| 675 """A list-specific equality assertion. | |
| 676 | |
| 677 Args: | |
| 678 list1: The first list to compare. | |
| 679 list2: The second list to compare. | |
| 680 msg: Optional message to use on failure instead of a list of | |
| 681 differences. | |
| 682 | |
| 683 """ | |
| 684 self.assertSequenceEquals(list1, list2, msg, seq_type=list) | |
| 685 | |
| 686 def assertTupleEquals(self, tuple1, tuple2, msg=None): | |
| 687 """A tuple-specific equality assertion. | |
| 688 | |
| 689 Args: | |
| 690 tuple1: The first tuple to compare. | |
| 691 tuple2: The second tuple to compare. | |
| 692 msg: Optional message to use on failure instead of a list of | |
| 693 differences. | |
| 694 """ | |
| 695 self.assertSequenceEquals(tuple1, tuple2, msg, seq_type=tuple) | |
| 696 | |
| 697 def assertSetEquals(self, set1, set2, msg=None): | |
| 698 """A set-specific equality assertion. | |
| 699 | |
| 700 Args: | |
| 701 set1: The first set to compare. | |
| 702 set2: The second set to compare. | |
| 703 msg: Optional message to use on failure instead of a list of | |
| 704 differences. | |
| 705 | |
| 706 For more general containership equality, assertSameElements will work | |
| 707 with things other than sets. This uses ducktyping to support | |
| 708 different types of sets, and is optimized for sets specifically | |
| 709 (parameters must support a difference method). | |
| 710 """ | |
| 711 try: | |
| 712 difference1 = set1.difference(set2) | |
| 713 except TypeError, e: | |
| 714 self.fail('invalid type when attempting set difference: %s' % e) | |
| 715 except AttributeError, e: | |
| 716 self.fail('first argument does not support set difference: %s' % e) | |
| 717 | |
| 718 try: | |
| 719 difference2 = set2.difference(set1) | |
| 720 except TypeError, e: | |
| 721 self.fail('invalid type when attempting set difference: %s' % e) | |
| 722 except AttributeError, e: | |
| 723 self.fail('second argument does not support set difference: %s' % e) | |
| 724 | |
| 725 if not (difference1 or difference2): | |
| 726 return | |
| 727 | |
| 728 if msg is not None: | |
| 729 self.fail(msg) | |
| 730 | |
| 731 lines = [] | |
| 732 if difference1: | |
| 733 lines.append('Items in the first set but not the second:') | |
| 734 for item in difference1: | |
| 735 lines.append(repr(item)) | |
| 736 if difference2: | |
| 737 lines.append('Items in the second set but not the first:') | |
| 738 for item in difference2: | |
| 739 lines.append(repr(item)) | |
| 740 self.fail('\n'.join(lines)) | |
| 741 | |
| 742 def assertIn(self, a, b, msg=None): | |
| 743 """Just like self.assert_(a in b), but with a nicer default message.""" | |
| 744 if msg is None: | |
| 745 msg = '"%s" not found in "%s"' % (a, b) | |
| 746 self.assert_(a in b, msg) | |
| 747 | |
| 748 def assertNotIn(self, a, b, msg=None): | |
| 749 """Just like self.assert_(a not in b), but with a nicer default message. """ | |
| 750 if msg is None: | |
| 751 msg = '"%s" unexpectedly found in "%s"' % (a, b) | |
| 752 self.assert_(a not in b, msg) | |
| 753 | |
| 754 def assertDictEquals(self, d1, d2, msg=None): | |
| 755 self.assert_(isinstance(d1, dict), 'First argument is not a dictionary') | |
| 756 self.assert_(isinstance(d2, dict), 'Second argument is not a dictionary' ) | |
| 757 | |
| 758 if d1 != d2: | |
| 759 self.fail(msg or ('\n' + '\n'.join(difflib.ndiff( | |
| 760 pprint.pformat(d1).splitlines(), | |
| 761 pprint.pformat(d2).splitlines())))) | |
| 762 | |
| 763 def assertDictContainsSubset(self, expected, actual, msg=None): | |
| 764 """Checks whether actual is a superset of expected.""" | |
| 765 missing = [] | |
| 766 mismatched = [] | |
| 767 for key, value in expected.iteritems(): | |
| 768 if key not in actual: | |
| 769 missing.append(key) | |
| 770 elif value != actual[key]: | |
| 771 mismatched.append('%s, expected: %s, actual: %s' % (key, value, | |
| 772 actual[key])) | |
| 773 | |
| 774 if not (missing or mismatched): | |
| 775 return | |
| 776 | |
| 777 missing_msg = mismatched_msg = '' | |
| 778 if missing: | |
| 779 missing_msg = 'Missing: %s' % ','.join(missing) | |
| 780 if mismatched: | |
| 781 mismatched_msg = 'Mismatched values: %s' % ','.join(mismatched) | |
| 782 | |
| 783 if msg: | |
| 784 msg = '%s: %s; %s' % (msg, missing_msg, mismatched_msg) | |
| 785 else: | |
| 786 msg = '%s; %s' % (missing_msg, mismatched_msg) | |
| 787 self.fail(msg) | |
| 788 | |
| 789 def assertSameElements(self, expected_seq, actual_seq, msg=None): | |
| 790 """An unordered sequence specific comparison. | |
| 791 | |
| 792 Raises with an error message listing which elements of expected_seq | |
| 793 are missing from actual_seq and vice versa if any. | |
| 794 """ | |
| 795 try: | |
| 796 expected = set(expected_seq) | |
| 797 actual = set(actual_seq) | |
| 798 missing = list(expected.difference(actual)) | |
| 799 unexpected = list(actual.difference(expected)) | |
| 800 missing.sort() | |
| 801 unexpected.sort() | |
| 802 except TypeError: | |
| 803 # Fall back to slower list-compare if any of the objects are | |
| 804 # not hashable. | |
| 805 expected = list(expected_seq) | |
| 806 actual = list(actual_seq) | |
| 807 expected.sort() | |
| 808 actual.sort() | |
| 809 missing, unexpected = _SortedListDifference(expected, actual) | |
| 810 errors = [] | |
| 811 if missing: | |
| 812 errors.append('Expected, but missing:\n %r\n' % missing) | |
| 813 if unexpected: | |
| 814 errors.append('Unexpected, but present:\n %r\n' % unexpected) | |
| 815 if errors: | |
| 816 self.fail(msg or ''.join(errors)) | |
| 817 | |
| 818 def assertMultiLineEquals(self, first, second, msg=None): | |
| 819 """Assert that two multi-line strings are equal.""" | |
| 820 self.assert_(isinstance(first, types.StringTypes), ( | |
| 821 'First argument is not a string')) | |
| 822 self.assert_(isinstance(second, types.StringTypes), ( | |
| 823 'Second argument is not a string')) | |
| 824 | |
| 825 if first != second: | |
| 826 raise self.failureException( | |
| 827 msg or '\n' + ''.join(difflib.ndiff(first.splitlines(True), | |
| 828 second.splitlines(True)))) | |
| 829 | |
| 830 def assertLess(self, a, b, msg=None): | |
| 831 """Just like self.assert_(a < b), but with a nicer default message.""" | |
| 832 if msg is None: | |
| 833 msg = '"%r" unexpectedly not less than "%r"' % (a, b) | |
| 834 self.assert_(a < b, msg) | |
| 835 | |
| 836 def assertLessEqual(self, a, b, msg=None): | |
| 837 """Just like self.assert_(a <= b), but with a nicer default message.""" | |
| 838 if msg is None: | |
| 839 msg = '"%r" unexpectedly not less than or equal to "%r"' % (a, b) | |
| 840 self.assert_(a <= b, msg) | |
| 841 | |
| 842 def assertGreater(self, a, b, msg=None): | |
| 843 """Just like self.assert_(a > b), but with a nicer default message.""" | |
| 844 if msg is None: | |
| 845 msg = '"%r" unexpectedly not greater than "%r"' % (a, b) | |
| 846 self.assert_(a > b, msg) | |
| 847 | |
| 848 def assertGreaterEqual(self, a, b, msg=None): | |
| 849 """Just like self.assert_(a >= b), but with a nicer default message.""" | |
| 850 if msg is None: | |
| 851 msg = '"%r" unexpectedly not greater than or equal to "%r"' % (a, b) | |
| 852 self.assert_(a >= b, msg) | |
| 853 | |
| 854 def assertIsNone(self, obj, msg=None): | |
| 855 """Same as self.assert_(obj is None), with a nicer default message.""" | |
| 856 if msg is None: | |
| 857 msg = '"%s" unexpectedly not None' % obj | |
| 858 self.assert_(obj is None, msg) | |
| 859 | |
| 860 def assertIsNotNone(self, obj, msg='unexpectedly None'): | |
| 861 """Included for symmetry with assertIsNone.""" | |
| 862 self.assert_(obj is not None, msg) | |
| 863 | |
| 864 def assertRaisesRegexp(self, expected_exception, expected_regexp, | |
| 865 callable_obj=None, *args, **kwargs): | |
| 866 """Asserts that the message in a raised exception matches a regexp. | |
| 867 | |
| 868 Args: | |
| 869 expected_exception: Exception class expected to be raised. | |
| 870 expected_regexp: Regexp (re pattern object or string) expected | |
| 871 to be found in error message. | |
| 872 callable_obj: Function to be called. | |
| 873 args: Extra args. | |
| 874 kwargs: Extra kwargs. | |
| 875 """ | |
| 876 context = AssertRaisesContext(expected_exception, self, expected_regexp) | |
| 877 if callable_obj is None: | |
| 878 return context | |
| 879 with context: | |
| 880 callable_obj(*args, **kwargs) | |
| 881 | |
| 882 def assertRegexpMatches(self, text, expected_regex, msg=None): | |
| 883 if isinstance(expected_regex, basestring): | |
| 884 expected_regex = re.compile(expected_regex) | |
| 885 if not expected_regex.search(text): | |
| 886 msg = msg or "Regexp didn't match" | |
| 887 msg = '%s: %r not found in %r' % (msg, expected_regex.pattern, text) | |
| 888 raise self.failureException(msg) | |
| 889 | |
| 890 | |
| 891 def _SortedListDifference(expected, actual): | |
| 892 """Finds elements in only one or the other of two, sorted input lists. | |
| 893 | |
| 894 Returns a two-element tuple of lists. The first list contains those | |
| 895 elements in the "expected" list but not in the "actual" list, and the | |
| 896 second contains those elements in the "actual" list but not in the | |
| 897 "expected" list. Duplicate elements in either input list are ignored. | |
| 898 """ | |
| 899 i = j = 0 | |
| 900 missing = [] | |
| 901 unexpected = [] | |
| 902 while True: | |
| 903 try: | |
| 904 e = expected[i] | |
| 905 a = actual[j] | |
| 906 if e < a: | |
| 907 missing.append(e) | |
| 908 i += 1 | |
| 909 while expected[i] == e: | |
| 910 i += 1 | |
| 911 elif e > a: | |
| 912 unexpected.append(a) | |
| 913 j += 1 | |
| 914 while actual[j] == a: | |
| 915 j += 1 | |
| 916 else: | |
| 917 i += 1 | |
| 918 try: | |
| 919 while expected[i] == e: | |
| 920 i += 1 | |
| 921 finally: | |
| 922 j += 1 | |
| 923 while actual[j] == a: | |
| 924 j += 1 | |
| 925 except IndexError: | |
| 926 missing.extend(expected[i:]) | |
| 927 unexpected.extend(actual[j:]) | |
| 928 break | |
| 929 return missing, unexpected | |
| 506 | 930 |
| 507 | 931 |
| 508 class TestSuite(object): | 932 class TestSuite(object): |
| 509 """A test suite is a composite test consisting of a number of TestCases. | 933 """A test suite is a composite test consisting of a number of TestCases. |
| 510 | 934 |
| 511 For use, create an instance of TestSuite, then add test case instances. | 935 For use, create an instance of TestSuite, then add test case instances. |
| 512 When all tests have been added, the suite can be passed to a test | 936 When all tests have been added, the suite can be passed to a test |
| 513 runner, such as TextTestRunner. It will run the individual test cases | 937 runner, such as TextTestRunner. It will run the individual test cases |
| 514 in the order in which they were added, aggregating the results. When | 938 in the order in which they were added, aggregating the results. When |
| 515 subclassing, do not forget to call the base class constructor. | 939 subclassing, do not forget to call the base class constructor. |
| (...skipping 526 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1042 | 1466 |
| 1043 main = TestProgram | 1467 main = TestProgram |
| 1044 | 1468 |
| 1045 | 1469 |
| 1046 ############################################################################## | 1470 ############################################################################## |
| 1047 # Executing this module from the command line | 1471 # Executing this module from the command line |
| 1048 ############################################################################## | 1472 ############################################################################## |
| 1049 | 1473 |
| 1050 if __name__ == "__main__": | 1474 if __name__ == "__main__": |
| 1051 main(module=None) | 1475 main(module=None) |
| OLD | NEW |