diff options
Diffstat (limited to 'Lib/unittest')
-rw-r--r-- | Lib/unittest/case.py | 54 | ||||
-rw-r--r-- | Lib/unittest/util.py | 37 |
2 files changed, 67 insertions, 24 deletions
diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index ea8fefa..2af48e1 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -8,8 +8,9 @@ import re import warnings from . import result -from .util import strclass, safe_repr, sorted_list_difference - +from .util import ( + strclass, safe_repr, sorted_list_difference, unorderable_list_difference +) class SkipTest(Exception): """ @@ -686,10 +687,9 @@ class TestCase(object): 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). + assertSetEqual 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) @@ -784,42 +784,48 @@ class TestCase(object): self.fail(self._formatMessage(msg, standardMsg)) - def assertSameElements(self, expected_seq, actual_seq, msg=None): - """An unordered sequence specific comparison. + def assertItemsEqual(self, expected_seq, actual_seq, msg=None): + """An unordered sequence / set specific comparison. It asserts that + expected_seq and actual_seq contain the same elements. It is + the equivalent of:: + + self.assertEqual(sorted(expected_seq), sorted(actual_seq)) Raises with an error message listing which elements of expected_seq are missing from actual_seq and vice versa if any. - Duplicate elements are ignored when comparing *expected_seq* and - *actual_seq*. It is the equivalent of ``assertEqual(set(expected), - set(actual))`` but it works with sequences of unhashable objects as - well. + 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. """ with warnings.catch_warnings(): if sys.py3kwarning: # Silence Py3k warning raised during the sorting for _msg in ["dict inequality comparisons", - "builtin_function_or_method order comparisons", - "comparing unequal types"]: + "builtin_function_or_method order comparisons", + "comparing unequal types"]: warnings.filterwarnings("ignore", _msg, DeprecationWarning) try: - expected = set(expected_seq) - actual = set(actual_seq) - missing = sorted(expected.difference(actual)) - unexpected = sorted(actual.difference(expected)) - except TypeError: - # Fall back to slower list-compare if any of the objects are - # not hashable. expected = sorted(expected_seq) actual = sorted(actual_seq) - missing, unexpected = sorted_list_difference(expected, actual) + except TypeError: + # Unsortable items (example: set(), complex(), ...) + expected = list(expected_seq) + actual = list(actual_seq) + missing, unexpected = unorderable_list_difference( + expected, actual, ignore_duplicate=False + ) + else: + return self.assertSequenceEqual(expected, actual, msg=msg) + errors = [] if missing: errors.append('Expected, but missing:\n %s' % - safe_repr(missing)) + safe_repr(missing)) if unexpected: errors.append('Unexpected, but present:\n %s' % - safe_repr(unexpected)) + safe_repr(unexpected)) if errors: standardMsg = '\n'.join(errors) self.fail(self._formatMessage(msg, standardMsg)) diff --git a/Lib/unittest/util.py b/Lib/unittest/util.py index 9fce625..9ddf879 100644 --- a/Lib/unittest/util.py +++ b/Lib/unittest/util.py @@ -48,3 +48,40 @@ def sorted_list_difference(expected, actual): unexpected.extend(actual[j:]) break return missing, unexpected + + +def unorderable_list_difference(expected, actual, ignore_duplicate=False): + """Same behavior as sorted_list_difference but + for lists of unorderable items (like dicts). + + As it does a linear search per item (remove) it + has O(n*n) performance. + """ + missing = [] + unexpected = [] + while expected: + item = expected.pop() + try: + actual.remove(item) + except ValueError: + missing.append(item) + if ignore_duplicate: + for lst in expected, actual: + try: + while True: + lst.remove(item) + except ValueError: + pass + if ignore_duplicate: + while actual: + item = actual.pop() + unexpected.append(item) + try: + while True: + actual.remove(item) + except ValueError: + pass + return missing, unexpected + + # anything left in actual is unexpected + return missing, actual |