|
|
| 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...) 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...) | | 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?
While you're at it, can you add a docstring?
gregory.p.smith
2009年03月31日 16:37:02
Done. We also decided that this should be private
On 2009年03月31日 15:01:06, GvR wrote:
> While you're at it, can you add a docstring?
Done. We also decided that this should be private so I've added an _.
| |
| 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 23 matching lines...) Loading... | |
| 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__ |
| 317 | 331 |
| 332 # Map types to custom assertEquals functions that will compare | |
|
GvR
2009年03月31日 15:01:06
assertEquals -> assertEqual
assertEquals -> assertEqual
gregory.p.smith
2009年03月31日 16:37:02
Done.
On 2009年03月31日 15:01:06, GvR wrote:
> assertEquals -> assertEqual
Done.
| |
| 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 | |
| 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 91 matching lines...) | | Loading... | |
| 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) |
| 451 | 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
assertSpamEqual
gregory.p.smith
2009年03月31日 16:37:02
Done.
On 2009年03月31日 15:01:06, GvR wrote:
> assertSpamEqual
Done.
| |
| 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'?
Mind dropping the trailing 's'?
gregory.p.smith
2009年03月31日 16:37:02
Done.
On 2009年03月31日 15:01:06, GvR wrote:
> Mind dropping the trailing 's'?
Done.
| |
| 522 """The default assertEquals implementation, not type specific.""" | |
| 523 if not first == second: | |
| 524 raise self.failureException(msg or '%r != %r' % (first, second)) | |
| 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
We had talked about making the 'def' define the recommended name, e.g.
assertNotEqual, and using aliases to keep the other names. Do you want to do
that at the same time as this change or in a separate one?
gregory.p.smith
2009年03月31日 16:37:02
Yes I'm going to do that in the next revision imme
On 2009年03月31日 15:01:06, GvR wrote:
> We had talked about making the 'def' define the recommended name, e.g.
> assertNotEqual, and using aliases to keep the other names. Do you want to do
> that at the same time as this change or in a separate one?
Yes I'm going to do that in the next revision immediately after this one.
| |
| 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 29 matching lines...) Loading... | |
| 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 |
| 506 | 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?
Drop the trailing 's' in the name?
gregory.p.smith
2009年03月31日 16:37:02
Done.
On 2009年03月31日 15:01:06, GvR wrote:
> Drop the trailing 's' in the name?
Done.
| |
| 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 | |
| 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. |
| 516 """ | 940 """ |
| (...skipping 525 matching lines...) | | 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 |