From e6e0e26780350b2c05bf21218c69667d0eb0992d Mon Sep 17 00:00:00 2001 From: Michael Foord Date: Sun, 19 Dec 2010 15:52:56 +0000 Subject: [PATCH] Issue 10242. Switching unittest.TestCase.assertItemsEqual to use a collections.Counter under the hood. This fixes bugs when comparing collections of items like sets that can be sorted without raising an exception but where sorting has no meaning. --- Lib/unittest/case.py | 28 ++++++++++++++-------------- Lib/unittest/test/test_case.py | 5 +++++ Misc/NEWS | 3 +++ 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index 33ab47a4b59..cd8f4fa0092 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -1,5 +1,6 @@ """Test case implementation""" +import collections import sys import functools import difflib @@ -850,14 +851,12 @@ class TestCase(object): self.fail(self._formatMessage(msg, standardMsg)) 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:: + """An unordered sequence specific comparison. It asserts that + actual_seq and expected_seq have the same element counts. + Equivalent to:: - 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. + self.assertEqual(Counter(iter(actual_seq)), + Counter(iter(expected_seq))) Asserts that each element has the same count in both sequences. Example: @@ -872,17 +871,18 @@ class TestCase(object): "comparing unequal types"]: warnings.filterwarnings("ignore", _msg, DeprecationWarning) try: - expected = sorted(expected_seq) - actual = sorted(actual_seq) + actual = collections.Counter(iter(actual_seq)) + expected = collections.Counter(iter(expected_seq)) 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 - ) + expected = list(expected_seq) + missing, unexpected = unorderable_list_difference(expected, actual) else: - return self.assertSequenceEqual(expected, actual, msg=msg) + if actual == expected: + return + missing = list(expected - actual) + unexpected = list(actual - expected) errors = [] if missing: diff --git a/Lib/unittest/test/test_case.py b/Lib/unittest/test/test_case.py index c42d98d82de..250e9053528 100644 --- a/Lib/unittest/test/test_case.py +++ b/Lib/unittest/test/test_case.py @@ -711,6 +711,11 @@ class Test_TestCase(unittest.TestCase, TestEquality, TestHashing): self.assertRaises(self.failureException, self.assertItemsEqual, [1, {'b': 2}, None, True], [{'b': 2}, True, None]) + # Same elements which don't reliably compare, in + # different order, see issue 10242 + a = [{2,4}, {1,2}] + b = a[::-1] + self.assertItemsEqual(a, b) def testAssertSetEqual(self): set1 = set() diff --git a/Misc/NEWS b/Misc/NEWS index b1b026545de..dc80eb8f620 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -22,6 +22,9 @@ Core and Builtins Library ------- +- Issue #10242: unittest.TestCase.assertItemsEqual makes too many assumgptions + about input. + - Issue #10611: SystemExit should not cause a unittest test run to exit. - Issue #6791: Limit header line length (to 65535 bytes) in http.client,