summaryrefslogtreecommitdiffstats
path: root/Lib/unittest
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/unittest')
-rw-r--r--Lib/unittest/case.py54
-rw-r--r--Lib/unittest/util.py37
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