Index: Lib/unittest.py =================================================================== --- Lib/unittest.py (revision 70726) +++ Lib/unittest.py (working copy) @@ -45,12 +45,15 @@ SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. ''' +import difflib +import functools +import os +import pprint +import re +import sys import time -import sys import traceback -import os import types -import functools ############################################################################## # Exported classes and functions @@ -315,6 +318,31 @@ (self.__class__, methodName)) self._testMethodDoc = testMethod.__doc__ + # Map types to custom assertEquals functions that will compare + # instances of said type in more detail to generate a more useful + # error message. + self.__type_equality_funcs = {} + self.addTypeEqualityFunc(dict, self.assertDictEqual) + self.addTypeEqualityFunc(list, self.assertListEqual) + self.addTypeEqualityFunc(tuple, self.assertTupleEqual) + self.addTypeEqualityFunc(set, self.assertSetEqual) + self.addTypeEqualityFunc(frozenset, self.assertSetEqual) + + def addTypeEqualityFunc(self, typeobj, function): + """Add a type specific assertEqual style function to compare a type. + + This method is for use by TestCase subclasses that need to register + their own type equality functions to provide nicer error messages. + + Args: + typeobj: The data type to call this function on when both values + are of the same type in assertEqual(). + function: The callable taking two arguments and an optional + msg= argument that raises self.failureException with a + useful error message when the two arguments are not equal. + """ + self.__type_equality_funcs[typeobj] = function + def setUp(self): "Hook method for setting up the test fixture before exercising it." pass @@ -330,15 +358,23 @@ return TestResult() def shortDescription(self): - """Returns a one-line description of the test, or None if no - description has been provided. + """Returns both the test method name and first line of its docstring. - The default implementation of this method returns the first line of - the specified test method's docstring. + If no docstring is given, only returns the method name. + + This method overrides unittest.TestCase.shortDescription(), which + only returns the first line of the docstring, obscuring the name + of the test upon failure. """ - doc = self._testMethodDoc - return doc and doc.split("\n")[0].strip() or None + desc = str(self) + doc_first_line = None + if self._testMethodDoc: + doc_first_line = self._testMethodDoc.split("\n")[0].strip() + if doc_first_line: + desc = '\n'.join((desc, doc_first_line)) + return desc + def id(self): return "%s.%s" % (_strclass(self.__class__), self._testMethodName) @@ -449,12 +485,39 @@ with context: callableObj(*args, **kwargs) + def _getAssertEqualityFunc(self, first, second): + """Get a detailed comparison function for the types of the two args. + + Returns: A callable accepting (first, second, msg=None) that will + raise a failure exception if first != second with a useful human + readable error message for those types. + """ + # + # NOTE(gregory.p.smith): I considered isinstance(first, type(second)) + # and vice versa. I opted for the conservative approach in case + # subclasses are not intended to be compared in detail to their super + # class instances using a type equality func. This means testing + # subtypes won't automagically use the detailed comparison. Callers + # should use their type specific assertSpamEquals method to compare + # subclasses if the detailed comparison is desired and appropriate. + # See the discussion in http://bugs.python.org/issue2578. + # + if type(first) is type(second): + return self.__type_equality_funcs.get(type(first), + self._baseAssertEquals) + return self._baseAssertEquals + + def _baseAssertEquals(self, first, second, msg=None): + """The default assertEquals implementation, not type specific.""" + if not first == second: + raise self.failureException(msg or '%r != %r' % (first, second)) + def failUnlessEqual(self, first, second, msg=None): """Fail if the two objects are unequal as determined by the '==' operator. """ - if not first == second: - raise self.failureException(msg or '%r != %r' % (first, second)) + assertion_func = self._getAssertEqualityFunc(first, second) + return assertion_func(first, second, msg=msg) def failIfEqual(self, first, second, msg=None): """Fail if the two objects are equal as determined by the '==' @@ -504,7 +567,356 @@ assertFalse = failIf + def assertSequenceEqual(self, seq1, seq2, msg=None, seq_type=None): + """An equality assertion for ordered sequences (like lists and tuples). + For the purposes of this function, a valid orderd sequence type is one + which can be indexed, has a length, and has an equality operator. + + Args: + seq1: The first sequence to compare. + seq2: The second sequence to compare. + seq_type: The expected datatype of the sequences, or None if no + datatype should be enforced. + msg: Optional message to use on failure instead of a list of + differences. + """ + if seq_type != None: + seq_type_name = seq_type.__name__ + assert isinstance(seq1, seq_type), ('First sequence is not' + ' a %s: %r' % (seq_type_name, + seq1)) + assert isinstance(seq2, seq_type), ('Second sequence is not' + ' a %s: %r' % (seq_type_name, + seq2)) + else: + seq_type_name = "sequence" + + differing = None + try: + len1 = len(seq1) + except (TypeError, NotImplementedError): + differing = 'First %s has no length. Non-sequence?' % ( + seq_type_name) + + if differing is None: + try: + len2 = len(seq2) + except (TypeError, NotImplementedError): + differing = 'Second %s has no length. Non-sequence?' % ( + seq_type_name) + + if differing is None: + if seq1 == seq2: + return + + for i in xrange(min(len1, len2)): + try: + item1 = seq1[i] + except (TypeError, IndexError, NotImplementedError): + differing = ('Unable to index element %d of first %s\n' % + (i, seq_type_name)) + break + + try: + item2 = seq2[i] + except (TypeError, IndexError, NotImplementedError): + differing = ('Unable to index element %d of second %s\n' % + (i, seq_type_name)) + break + + if item1 != item2: + differing = ('First differing element %d:\n%s\n%s\n' % + (i, item1, item2)) + break + else: + if (len1 == len2 and seq_type is None and + type(seq1) != type(seq2)): + # The sequences are the same, but have differing types. + return + # A catch-all message for handling arbitrary user-defined + # sequences. + differing = '%ss differ:\n' % seq_type_name.capitalize() + if len1 > len2: + differing = ('First %s contains %d additional ' + 'elements.\n' % (seq_type_name, len1 - len2)) + try: + differing += ('First extra element %d:\n%s\n' % + (len2, seq1[len2])) + except (TypeError, IndexError, NotImplementedError): + differing += ('Unable to index element %d ' + 'of first %s\n' % (len2, seq_type_name)) + elif len1 < len2: + differing = ('Second %s contains %d additional ' + 'elements.\n' % (seq_type_name, len2 - len1)) + try: + differing += ('First extra element %d:\n%s\n' % + (len1, seq2[len1])) + except (TypeError, IndexError, NotImplementedError): + differing += ('Unable to index element %d ' + 'of second %s\n' % (len1, seq_type_name)) + if not msg: + msg = '\n'.join(difflib.ndiff(pprint.pformat(seq1).splitlines(), + pprint.pformat(seq2).splitlines())) + self.fail(differing + msg) + + def assertListEqual(self, list1, list2, msg=None): + """A list-specific equality assertion. + + Args: + list1: The first list to compare. + list2: The second list to compare. + msg: Optional message to use on failure instead of a list of + differences. + + """ + self.assertSequenceEqual(list1, list2, msg, seq_type=list) + + def assertTupleEqual(self, tuple1, tuple2, msg=None): + """A tuple-specific equality assertion. + + Args: + tuple1: The first tuple to compare. + tuple2: The second tuple to compare. + msg: Optional message to use on failure instead of a list of + differences. + """ + self.assertSequenceEqual(tuple1, tuple2, msg, seq_type=tuple) + + def assertSetEqual(self, set1, set2, msg=None): + """A set-specific equality assertion. + + Args: + set1: The first set to compare. + set2: The second set to compare. + msg: Optional message to use on failure instead of a list of + differences. + + For more general containership equality, assertSameElements will work + with things other than sets. This uses ducktyping to support + different types of sets, and is optimized for sets specifically + (parameters must support a difference method). + """ + try: + difference1 = set1.difference(set2) + except TypeError, e: + self.fail('invalid type when attempting set difference: %s' % e) + except AttributeError, e: + self.fail('first argument does not support set difference: %s' % e) + + try: + difference2 = set2.difference(set1) + except TypeError, e: + self.fail('invalid type when attempting set difference: %s' % e) + except AttributeError, e: + self.fail('second argument does not support set difference: %s' % e) + + if not (difference1 or difference2): + return + + if msg is not None: + self.fail(msg) + + lines = [] + if difference1: + lines.append('Items in the first set but not the second:') + for item in difference1: + lines.append(repr(item)) + if difference2: + lines.append('Items in the second set but not the first:') + for item in difference2: + lines.append(repr(item)) + self.fail('\n'.join(lines)) + + def assertIn(self, a, b, msg=None): + """Just like self.assert_(a in b), but with a nicer default message.""" + if msg is None: + msg = '"%s" not found in "%s"' % (a, b) + self.assert_(a in b, msg) + + def assertNotIn(self, a, b, msg=None): + """Just like self.assert_(a not in b), but with a nicer default message.""" + if msg is None: + msg = '"%s" unexpectedly found in "%s"' % (a, b) + self.assert_(a not in b, msg) + + def assertDictEqual(self, d1, d2, msg=None): + assert isinstance(d1, dict), 'First argument is not a dictionary' + assert isinstance(d2, dict), 'Second argument is not a dictionary' + + if d1 != d2: + self.fail(msg or ('\n' + '\n'.join(difflib.ndiff( + pprint.pformat(d1).splitlines(), + pprint.pformat(d2).splitlines())))) + + def assertDictContainsSubset(self, expected, actual, msg=None): + """Checks whether actual is a superset of expected.""" + missing = [] + mismatched = [] + for key, value in expected.iteritems(): + if key not in actual: + missing.append(key) + elif value != actual[key]: + mismatched.append('%s, expected: %s, actual: %s' % (key, value, + actual[key])) + + if not (missing or mismatched): + return + + missing_msg = mismatched_msg = '' + if missing: + missing_msg = 'Missing: %s' % ','.join(missing) + if mismatched: + mismatched_msg = 'Mismatched values: %s' % ','.join(mismatched) + + if msg: + msg = '%s: %s; %s' % (msg, missing_msg, mismatched_msg) + else: + msg = '%s; %s' % (missing_msg, mismatched_msg) + self.fail(msg) + + def assertSameElements(self, expected_seq, actual_seq, msg=None): + """An unordered sequence specific comparison. + + Raises with an error message listing which elements of expected_seq + are missing from actual_seq and vice versa if any. + """ + try: + expected = set(expected_seq) + actual = set(actual_seq) + missing = list(expected.difference(actual)) + unexpected = list(actual.difference(expected)) + missing.sort() + unexpected.sort() + except TypeError: + # Fall back to slower list-compare if any of the objects are + # not hashable. + expected = list(expected_seq) + actual = list(actual_seq) + expected.sort() + actual.sort() + missing, unexpected = _SortedListDifference(expected, actual) + errors = [] + if missing: + errors.append('Expected, but missing:\n %r\n' % missing) + if unexpected: + errors.append('Unexpected, but present:\n %r\n' % unexpected) + if errors: + self.fail(msg or ''.join(errors)) + + def assertMultiLineEqual(self, first, second, msg=None): + """Assert that two multi-line strings are equal.""" + assert isinstance(first, types.StringTypes), ( + 'First argument is not a string') + assert isinstance(second, types.StringTypes), ( + 'Second argument is not a string') + + if first != second: + raise self.failureException( + msg or '\n' + ''.join(difflib.ndiff(first.splitlines(True), + second.splitlines(True)))) + + def assertLess(self, a, b, msg=None): + """Just like self.assert_(a < b), but with a nicer default message.""" + if msg is None: + msg = '"%r" unexpectedly not less than "%r"' % (a, b) + self.assert_(a < b, msg) + + def assertLessEqual(self, a, b, msg=None): + """Just like self.assert_(a <= b), but with a nicer default message.""" + if msg is None: + msg = '"%r" unexpectedly not less than or equal to "%r"' % (a, b) + self.assert_(a <= b, msg) + + def assertGreater(self, a, b, msg=None): + """Just like self.assert_(a > b), but with a nicer default message.""" + if msg is None: + msg = '"%r" unexpectedly not greater than "%r"' % (a, b) + self.assert_(a > b, msg) + + def assertGreaterEqual(self, a, b, msg=None): + """Just like self.assert_(a >= b), but with a nicer default message.""" + if msg is None: + msg = '"%r" unexpectedly not greater than or equal to "%r"' % (a, b) + self.assert_(a >= b, msg) + + def assertIsNone(self, obj, msg=None): + """Same as self.assert_(obj is None), with a nicer default message.""" + if msg is None: + msg = '"%s" unexpectedly not None' % obj + self.assert_(obj is None, msg) + + def assertIsNotNone(self, obj, msg='unexpectedly None'): + """Included for symmetry with assertIsNone.""" + self.assert_(obj is not None, msg) + + def assertRaisesWithRegexpMatch(self, expected_exception, expected_regexp, + callable_obj, *args, **kwargs): + """Asserts that the message in a raised exception matches a regexp. + + Args: + expected_exception: Exception class expected to be raised. + expected_regexp: Regexp (re pattern object or string) expected + to be found in error message. + callable_obj: Function to be called. + args: Extra args. + kwargs: Extra kwargs. + """ + try: + callable_obj(*args, **kwargs) + except expected_exception, err: + if isinstance(expected_regexp, basestring): + expected_regexp = re.compile(expected_regexp) + self.assert_(expected_regexp.search(str(err)), + '"%s" does not match "%s"' % + (expected_regexp.pattern, str(err))) + else: + self.fail(expected_exception.__name__ + ' not raised') + + + + +def _SortedListDifference(expected, actual): + """Finds elements in only one or the other of two, sorted input lists. + + Returns a two-element tuple of lists. The first list contains those + elements in the "expected" list but not in the "actual" list, and the + second contains those elements in the "actual" list but not in the + "expected" list. Duplicate elements in either input list are ignored. + """ + i = j = 0 + missing = [] + unexpected = [] + while True: + try: + e = expected[i] + a = actual[j] + if e < a: + missing.append(e) + i += 1 + while expected[i] == e: + i += 1 + elif e > a: + unexpected.append(a) + j += 1 + while actual[j] == a: + j += 1 + else: + i += 1 + try: + while expected[i] == e: + i += 1 + finally: + j += 1 + while actual[j] == a: + j += 1 + except IndexError: + missing.extend(expected[i:]) + unexpected.extend(actual[j:]) + break + return missing, unexpected + + class TestSuite(object): """A test suite is a composite test consisting of a number of TestCases. Index: Lib/test/test_gc.py =================================================================== --- Lib/test/test_gc.py (revision 70726) +++ Lib/test/test_gc.py (working copy) @@ -244,7 +244,7 @@ # - the call to assertEqual somehow avoids building its args tuple def test_get_count(self): # Avoid future allocation of method object - assertEqual = self.assertEqual + assertEqual = self._baseAssertEquals gc.collect() assertEqual(gc.get_count(), (0, 0, 0)) a = dict() Index: Lib/test/test_struct.py =================================================================== --- Lib/test/test_struct.py (revision 70726) +++ Lib/test/test_struct.py (working copy) @@ -227,6 +227,7 @@ BUGGY_RANGE_CHECK = "bBhHiIlL" def __init__(self, formatpair, bytesize): + super(IntTester, self).__init__(methodName='test_one') self.assertEqual(len(formatpair), 2) self.formatpair = formatpair for direction in "<>!=": Index: Lib/test/test_unittest.py =================================================================== --- Lib/test/test_unittest.py (revision 70726) +++ Lib/test/test_unittest.py (working copy) @@ -6,6 +6,7 @@ TestCase.{assert,fail}* methods (some are tested implicitly) """ +import re from test import test_support import unittest from unittest import TestCase @@ -2237,39 +2238,6 @@ self.failUnless(isinstance(Foo().id(), basestring)) - # "Returns a one-line description of the test, or None if no description - # has been provided. The default implementation of this method returns - # the first line of the test method's docstring, if available, or None." - def test_shortDescription__no_docstring(self): - class Foo(unittest.TestCase): - def runTest(self): - pass - - self.assertEqual(Foo().shortDescription(), None) - - # "Returns a one-line description of the test, or None if no description - # has been provided. The default implementation of this method returns - # the first line of the test method's docstring, if available, or None." - def test_shortDescription__singleline_docstring(self): - class Foo(unittest.TestCase): - def runTest(self): - "this tests foo" - pass - - self.assertEqual(Foo().shortDescription(), "this tests foo") - - # "Returns a one-line description of the test, or None if no description - # has been provided. The default implementation of this method returns - # the first line of the test method's docstring, if available, or None." - def test_shortDescription__multiline_docstring(self): - class Foo(unittest.TestCase): - def runTest(self): - """this tests foo - blah, bar and baz are also tested""" - pass - - self.assertEqual(Foo().shortDescription(), "this tests foo") - # "If result is omitted or None, a temporary result object is created # and used, but is not made available to the caller" def test_run__uses_defaultTestResult(self): @@ -2288,7 +2256,389 @@ expected = ['startTest', 'test', 'addSuccess', 'stopTest'] self.assertEqual(events, expected) + def testShortDescriptionWithoutDocstring(self): + self.assertEquals( + self.shortDescription(), + 'testShortDescriptionWithoutDocstring (' + __name__ + + '.Test_TestCase)') + def testShortDescriptionWithOneLineDocstring(self): + """Tests shortDescription() for a method with a docstring.""" + self.assertEquals( + self.shortDescription(), + ('testShortDescriptionWithOneLineDocstring ' + '(' + __name__ + '.Test_TestCase)\n' + 'Tests shortDescription() for a method with a docstring.')) + + def testShortDescriptionWithMultiLineDocstring(self): + """Tests shortDescription() for a method with a longer docstring. + + This method ensures that only the first line of a docstring is + returned used in the short description, no matter how long the + whole thing is. + """ + self.assertEquals( + self.shortDescription(), + ('testShortDescriptionWithMultiLineDocstring ' + '(' + __name__ + '.Test_TestCase)\n' + 'Tests shortDescription() for a method with a longer ' + 'docstring.')) + + + def testAddTypeEqualityFunc(self): + class SadSnake(object): + """Dummy class for test_addTypeEqualityFunc.""" + s1, s2 = SadSnake(), SadSnake() + self.assertFalse(s1 == s2) + def AllSnakesCreatedEqual(a, b, msg=None): + return type(a) == type(b) == SadSnake + self.addTypeEqualityFunc(SadSnake, AllSnakesCreatedEqual) + self.assertEqual(s1, s2) + # No this doesn't clean up and remove the SadSnake equality func + # from this TestCase instance but since its a local nothing else + # will ever notice that. + + def testAssertIn(self): + animals = {'monkey': 'banana', 'cow': 'grass', 'seal': 'fish'} + + self.assertIn('a', 'abc') + self.assertIn(2, [1, 2, 3]) + self.assertIn('monkey', animals) + + self.assertNotIn('d', 'abc') + self.assertNotIn(0, [1, 2, 3]) + self.assertNotIn('otter', animals) + + self.assertRaises(AssertionError, self.assertIn, 'x', 'abc') + self.assertRaises(AssertionError, self.assertIn, 4, [1, 2, 3]) + self.assertRaises(AssertionError, self.assertIn, 'elephant', animals) + + self.assertRaises(AssertionError, self.assertNotIn, 'c', 'abc') + self.assertRaises(AssertionError, self.assertNotIn, 1, [1, 2, 3]) + self.assertRaises(AssertionError, self.assertNotIn, 'cow', animals) + + def testAssertDictContainsSubset(self): + self.assertDictContainsSubset({}, {}) + self.assertDictContainsSubset({}, {'a': 1}) + self.assertDictContainsSubset({'a': 1}, {'a': 1}) + self.assertDictContainsSubset({'a': 1}, {'a': 1, 'b': 2}) + self.assertDictContainsSubset({'a': 1, 'b': 2}, {'a': 1, 'b': 2}) + + self.assertRaises(unittest.TestCase.failureException, + self.assertDictContainsSubset, {'a': 2}, {'a': 1}, + '.*Mismatched values:.*') + + self.assertRaises(unittest.TestCase.failureException, + self.assertDictContainsSubset, {'c': 1}, {'a': 1}, + '.*Missing:.*') + + self.assertRaises(unittest.TestCase.failureException, + self.assertDictContainsSubset, {'a': 1, 'c': 1}, + {'a': 1}, '.*Missing:.*') + + self.assertRaises(unittest.TestCase.failureException, + self.assertDictContainsSubset, {'a': 1, 'c': 1}, + {'a': 1}, '.*Missing:.*Mismatched values:.*') + + def testAssertEqual(self): + equal_pairs = [ + ((), ()), + ({}, {}), + ([], []), + (set(), set()), + (frozenset(), frozenset())] + for a, b in equal_pairs: + if self.assertEqual(a, b) is not None: + self.fail('assertEqual(%r, %r) failed' % (a, b)) + if self.assertEqual(a, b, msg='foo') is not None: + self.fail('assertEqual(%r, %r) with msg= failed' % (a, b)) + if self.assertEqual(a, b, 'foo') is not None: + self.fail('assertEqual(%r, %r) with third parameter failed' % + (a, b)) + + unequal_pairs = [ + ((), []), + ({}, set()), + (set([4,1]), frozenset([4,2])), + (frozenset([4,5]), set([2,3])), + (set([3,4]), set([5,4]))] + for a, b in unequal_pairs: + self.assertRaises(self.failureException, self.assertEqual, a, b) + self.assertRaises(self.failureException, self.assertEqual, a, b, + 'foo') + self.assertRaises(self.failureException, self.assertEqual, a, b, + msg='foo') + + def testEquality(self): + self.assertListEqual([], []) + self.assertTupleEqual((), ()) + self.assertSequenceEqual([], ()) + + a = [0, 'a', []] + b = [] + self.assertRaises(unittest.TestCase.failureException, + self.assertListEqual, a, b) + self.assertRaises(unittest.TestCase.failureException, + self.assertListEqual, tuple(a), tuple(b)) + self.assertRaises(unittest.TestCase.failureException, + self.assertSequenceEqual, a, tuple(b)) + + b.extend(a) + self.assertListEqual(a, b) + self.assertTupleEqual(tuple(a), tuple(b)) + self.assertSequenceEqual(a, tuple(b)) + self.assertSequenceEqual(tuple(a), b) + + self.assertRaises(AssertionError, self.assertListEqual, a, tuple(b)) + self.assertRaises(AssertionError, self.assertTupleEqual, tuple(a), b) + self.assertRaises(AssertionError, self.assertListEqual, None, b) + self.assertRaises(AssertionError, self.assertTupleEqual, None, + tuple(b)) + self.assertRaises(AssertionError, self.assertSequenceEqual, None, + tuple(b)) + self.assertRaises(AssertionError, self.assertListEqual, 1, 1) + self.assertRaises(AssertionError, self.assertTupleEqual, 1, 1) + self.assertRaises(AssertionError, self.assertSequenceEqual, 1, 1) + + self.assertDictEqual({}, {}) + + c = { 'x': 1 } + d = {} + self.assertRaises(unittest.TestCase.failureException, + self.assertDictEqual, c, d) + + d.update(c) + self.assertDictEqual(c, d) + + d['x'] = 0 + self.assertRaises(unittest.TestCase.failureException, + self.assertDictEqual, c, d, 'These are unequal') + + self.assertRaises(AssertionError, self.assertDictEqual, None, d) + self.assertRaises(AssertionError, self.assertDictEqual, [], d) + self.assertRaises(AssertionError, self.assertDictEqual, 1, 1) + + self.assertSameElements([1, 2, 3], [3, 2, 1]) + self.assertSameElements([1, 2] + [3] * 100, [1] * 100 + [2, 3]) + self.assertSameElements(['foo', 'bar', 'baz'], ['bar', 'baz', 'foo']) + self.assertRaises(AssertionError, self.assertSameElements, + [10], [10, 11]) + self.assertRaises(AssertionError, self.assertSameElements, + [10, 11], [10]) + + # Test that sequences of unhashable objects can be tested for sameness: + self.assertSameElements([[1, 2], [3, 4]], [[3, 4], [1, 2]]) + self.assertSameElements([{'a': 1}, {'b': 2}], [{'b': 2}, {'a': 1}]) + self.assertRaises(AssertionError, self.assertSameElements, [[1]], [[2]]) + + def testAssertSetEqual(self): + set1 = set() + set2 = set() + self.assertSetEqual(set1, set2) + + self.assertRaises(AssertionError, self.assertSetEqual, None, set2) + self.assertRaises(AssertionError, self.assertSetEqual, [], set2) + self.assertRaises(AssertionError, self.assertSetEqual, set1, None) + self.assertRaises(AssertionError, self.assertSetEqual, set1, []) + + set1 = set(['a']) + set2 = set() + self.assertRaises(AssertionError, self.assertSetEqual, set1, set2) + + set1 = set(['a']) + set2 = set(['a']) + self.assertSetEqual(set1, set2) + + set1 = set(['a']) + set2 = set(['a', 'b']) + self.assertRaises(AssertionError, self.assertSetEqual, set1, set2) + + set1 = set(['a']) + set2 = frozenset(['a', 'b']) + self.assertRaises(AssertionError, self.assertSetEqual, set1, set2) + + set1 = set(['a', 'b']) + set2 = frozenset(['a', 'b']) + self.assertSetEqual(set1, set2) + + set1 = set() + set2 = "foo" + self.assertRaises(AssertionError, self.assertSetEqual, set1, set2) + self.assertRaises(AssertionError, self.assertSetEqual, set2, set1) + + # make sure any string formatting is tuple-safe + set1 = set([(0, 1), (2, 3)]) + set2 = set([(4, 5)]) + self.assertRaises(AssertionError, self.assertSetEqual, set1, set2) + + def testInequality(self): + # Try ints + self.assertGreater(2, 1) + self.assertGreaterEqual(2, 1) + self.assertGreaterEqual(1, 1) + self.assertLess(1, 2) + self.assertLessEqual(1, 2) + self.assertLessEqual(1, 1) + self.assertRaises(AssertionError, self.assertGreater, 1, 2) + self.assertRaises(AssertionError, self.assertGreater, 1, 1) + self.assertRaises(AssertionError, self.assertGreaterEqual, 1, 2) + self.assertRaises(AssertionError, self.assertLess, 2, 1) + self.assertRaises(AssertionError, self.assertLess, 1, 1) + self.assertRaises(AssertionError, self.assertLessEqual, 2, 1) + + # Try Floats + self.assertGreater(1.1, 1.0) + self.assertGreaterEqual(1.1, 1.0) + self.assertGreaterEqual(1.0, 1.0) + self.assertLess(1.0, 1.1) + self.assertLessEqual(1.0, 1.1) + self.assertLessEqual(1.0, 1.0) + self.assertRaises(AssertionError, self.assertGreater, 1.0, 1.1) + self.assertRaises(AssertionError, self.assertGreater, 1.0, 1.0) + self.assertRaises(AssertionError, self.assertGreaterEqual, 1.0, 1.1) + self.assertRaises(AssertionError, self.assertLess, 1.1, 1.0) + self.assertRaises(AssertionError, self.assertLess, 1.0, 1.0) + self.assertRaises(AssertionError, self.assertLessEqual, 1.1, 1.0) + + # Try Strings + self.assertGreater('bug', 'ant') + self.assertGreaterEqual('bug', 'ant') + self.assertGreaterEqual('ant', 'ant') + self.assertLess('ant', 'bug') + self.assertLessEqual('ant', 'bug') + self.assertLessEqual('ant', 'ant') + self.assertRaises(AssertionError, self.assertGreater, 'ant', 'bug') + self.assertRaises(AssertionError, self.assertGreater, 'ant', 'ant') + self.assertRaises(AssertionError, self.assertGreaterEqual, 'ant', 'bug') + self.assertRaises(AssertionError, self.assertLess, 'bug', 'ant') + self.assertRaises(AssertionError, self.assertLess, 'ant', 'ant') + self.assertRaises(AssertionError, self.assertLessEqual, 'bug', 'ant') + + # Try Unicode + self.assertGreater(u'bug', u'ant') + self.assertGreaterEqual(u'bug', u'ant') + self.assertGreaterEqual(u'ant', u'ant') + self.assertLess(u'ant', u'bug') + self.assertLessEqual(u'ant', u'bug') + self.assertLessEqual(u'ant', u'ant') + self.assertRaises(AssertionError, self.assertGreater, u'ant', u'bug') + self.assertRaises(AssertionError, self.assertGreater, u'ant', u'ant') + self.assertRaises(AssertionError, self.assertGreaterEqual, u'ant', + u'bug') + self.assertRaises(AssertionError, self.assertLess, u'bug', u'ant') + self.assertRaises(AssertionError, self.assertLess, u'ant', u'ant') + self.assertRaises(AssertionError, self.assertLessEqual, u'bug', u'ant') + + # Try Mixed String/Unicode + self.assertGreater('bug', u'ant') + self.assertGreater(u'bug', 'ant') + self.assertGreaterEqual('bug', u'ant') + self.assertGreaterEqual(u'bug', 'ant') + self.assertGreaterEqual('ant', u'ant') + self.assertGreaterEqual(u'ant', 'ant') + self.assertLess('ant', u'bug') + self.assertLess(u'ant', 'bug') + self.assertLessEqual('ant', u'bug') + self.assertLessEqual(u'ant', 'bug') + self.assertLessEqual('ant', u'ant') + self.assertLessEqual(u'ant', 'ant') + self.assertRaises(AssertionError, self.assertGreater, 'ant', u'bug') + self.assertRaises(AssertionError, self.assertGreater, u'ant', 'bug') + self.assertRaises(AssertionError, self.assertGreater, 'ant', u'ant') + self.assertRaises(AssertionError, self.assertGreater, u'ant', 'ant') + self.assertRaises(AssertionError, self.assertGreaterEqual, 'ant', + u'bug') + self.assertRaises(AssertionError, self.assertGreaterEqual, u'ant', + 'bug') + self.assertRaises(AssertionError, self.assertLess, 'bug', u'ant') + self.assertRaises(AssertionError, self.assertLess, u'bug', 'ant') + self.assertRaises(AssertionError, self.assertLess, 'ant', u'ant') + self.assertRaises(AssertionError, self.assertLess, u'ant', 'ant') + self.assertRaises(AssertionError, self.assertLessEqual, 'bug', u'ant') + self.assertRaises(AssertionError, self.assertLessEqual, u'bug', 'ant') + + def testAssertMultiLineEqual(self): + sample_text = """\ +http://www.python.org/doc/2.3/lib/module-unittest.html +test case + A test case is the smallest unit of testing. [...] +""" + revised_sample_text = """\ +http://www.python.org/doc/2.4.1/lib/module-unittest.html +test case + A test case is the smallest unit of testing. [...] You may provide your + own implementation that does not subclass from TestCase, of course. +""" + sample_text_error = """ +- http://www.python.org/doc/2.3/lib/module-unittest.html +? ^ ++ http://www.python.org/doc/2.4.1/lib/module-unittest.html +? ^^^ + test case +- A test case is the smallest unit of testing. [...] ++ A test case is the smallest unit of testing. [...] You may provide your +? +++++++++++++++++++++ ++ own implementation that does not subclass from TestCase, of course. +""" + + for type1 in (str, unicode): + for type2 in (str, unicode): + try: + self.assertMultiLineEqual(type1(sample_text), + type2(revised_sample_text)) + except AssertionError, e: + self.assertEqual(sample_text_error, str(e)) + + def testAssertIsNone(self): + self.assertIsNone(None) + self.assertRaises(AssertionError, self.assertIsNone, False) + self.assertIsNotNone('Google') + self.assertRaises(AssertionError, self.assertIsNotNone, None) + + def testAssertRaisesWithRegexpMatch(self): + class ExceptionMock(Exception): + pass + + def Stub(): + raise ExceptionMock('We expect') + + self.assertRaisesWithRegexpMatch(ExceptionMock, re.compile('expect$'), + Stub) + self.assertRaisesWithRegexpMatch(ExceptionMock, 'expect$', Stub) + self.assertRaisesWithRegexpMatch(ExceptionMock, u'expect$', Stub) + + def testAssertNotRaisesWithRegexpMatch(self): + self.assertRaisesWithRegexpMatch( + AssertionError, '^Exception not raised$', + self.assertRaisesWithRegexpMatch, Exception, re.compile('x'), + lambda: None) + self.assertRaisesWithRegexpMatch( + AssertionError, '^Exception not raised$', + self.assertRaisesWithRegexpMatch, Exception, 'x', + lambda: None) + self.assertRaisesWithRegexpMatch( + AssertionError, '^Exception not raised$', + self.assertRaisesWithRegexpMatch, Exception, u'x', + lambda: None) + + def testAssertRaisesWithRegexpMismatch(self): + def Stub(): + raise Exception('Unexpected') + + self.assertRaisesWithRegexpMatch( + AssertionError, r'"\^Expected\$" does not match "Unexpected"', + self.assertRaisesWithRegexpMatch, Exception, '^Expected$', + Stub) + self.assertRaisesWithRegexpMatch( + AssertionError, r'"\^Expected\$" does not match "Unexpected"', + self.assertRaisesWithRegexpMatch, Exception, u'^Expected$', + Stub) + self.assertRaisesWithRegexpMatch( + AssertionError, r'"\^Expected\$" does not match "Unexpected"', + self.assertRaisesWithRegexpMatch, Exception, + re.compile('^Expected$'), Stub) + + class Test_TestSkipping(TestCase): def test_skipping(self):