Index: Lib/unittest/case.py =================================================================== --- Lib/unittest/case.py (revision 77904) +++ Lib/unittest/case.py (working copy) @@ -36,6 +36,16 @@ """ pass +class _SafeKey(object): + """ + Helper class for key functions when sorting unorderable objects. + """ + def __init__(self, obj): + self.obj = obj + + def __lt__(self, other): + return cmp(self.obj, other.obj) == -1 + def _id(obj): return obj @@ -603,6 +613,18 @@ msg = self._formatMessage(msg, standardMsg) self.fail(msg) + def assertItemsEqual(self, expected_seq, actual_seq, msg=None): + """An unordered sequence/set specific comparison. + + Asserts that each element has the same count in both sequences. + Example: + - [0, 1, 1] and [1, 0, 1] compare equal. + - [0, 0, 1] and [0, 1] compare unequal. + """ + expected = sorted(expected_seq, key=_SafeKey) + actual = sorted(actual_seq, key=_SafeKey) + self.assertSequenceEqual(expected, actual, msg=msg) + def assertListEqual(self, list1, list2, msg=None): """A list-specific equality assertion. @@ -729,10 +751,12 @@ self.fail(self._formatMessage(msg, standardMsg)) def assertSameElements(self, expected_seq, actual_seq, msg=None): - """An unordered sequence specific comparison. + """An unordered sequence/set specific comparison. Raises with an error message listing which elements of expected_seq are missing from actual_seq and vice versa if any. + Example: + - [1, 0] and [0, 0, 1] and [True, False] compare equal. """ try: expected = set(expected_seq) Index: Lib/test/test_unittest.py =================================================================== --- Lib/test/test_unittest.py (revision 77904) +++ Lib/test/test_unittest.py (working copy) @@ -2646,21 +2646,56 @@ self.assertRaises(self.failureException, self.assertDictEqual, [], d) self.assertRaises(self.failureException, self.assertDictEqual, 1, 1) + # Compare sets of elements 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.assertSameElements([a, a, 2, 2, 3], (a, 2, 3, a, 2)) + self.assertSameElements([1, "2", "a", "a"], ["a", "2", True, 1]) self.assertRaises(self.failureException, self.assertSameElements, [10], [10, 11]) self.assertRaises(self.failureException, 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([0, [1, 2], [3, 4]], [[3, 4], False, [1, 2]]) self.assertSameElements([{'a': 1}, {'b': 2}], [{'b': 2}, {'a': 1}]) self.assertRaises(self.failureException, self.assertSameElements, [[1]], [[2]]) + # Same elements, but not same sequence length + self.assertSameElements([1, 1, 2], [2, 1]) + self.assertSameElements([1, 1, "2", "a", "a"], ["2", "2", True, "a"]) + self.assertSameElements([1, {'b': 2}, None, True], [{'b': 2}, True, None]) + + # Compare unordered sequences of items + self.assertItemsEqual([1, 2] + [3] * 100, [3] * 100 + [2, 1]) + self.assertItemsEqual(['foo', 'bar', 'baz'], ['bar', 'baz', 'foo']) + self.assertItemsEqual([a, a, 2, 2, 3], (a, 2, 3, a, 2)) + self.assertItemsEqual([1, "2", "a", "a"], ["a", "2", True, "a"]) + self.assertRaises(self.failureException, self.assertItemsEqual, + [10], [10, 11]) + self.assertRaises(self.failureException, self.assertItemsEqual, + [10, 11], [10]) + self.assertRaises(self.failureException, self.assertItemsEqual, + [1, 2, 3, 3], [1, 1, 2, 3]) + + # Test that sequences of unhashable objects can be tested for sameness: + self.assertItemsEqual([0, [1, 2], [3, 4]], [[3, 4], False, [1, 2]]) + + self.assertItemsEqual([{'a': 1}, {'b': 2}], [{'b': 2}, {'a': 1}]) + self.assertRaises(self.failureException, self.assertItemsEqual, + [[1]], [[2]]) + + # Same elements, but not same sequence length + self.assertRaises(self.failureException, self.assertItemsEqual, + [1, 1, 2], [2, 1]) + self.assertRaises(self.failureException, self.assertItemsEqual, + [1, 1, "2", "a", "a"], ["2", "2", True, "a"]) + self.assertRaises(self.failureException, self.assertItemsEqual, + [1, {'b': 2}, None, True], [{'b': 2}, True, None]) + def testAssertSetEqual(self): set1 = set() set2 = set()