Skip to content

Commit ece7121

Browse files
gh-145546: unittest.util: fix sorted_list_difference tail deduplication (GH-145547)
* fix(unittest.util): Deduplicate tail elements in sorted_list_difference sorted_list_difference failed to deduplicate remaining elements when one list was exhausted, causing duplicate values in the result. Deduplicate before extending. Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
1 parent 5d6e8dd commit ece7121

File tree

3 files changed

+45
-2
lines changed

3 files changed

+45
-2
lines changed

Lib/test/test_unittest/test_util.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,39 @@ def test_sorted_list_difference(self):
2626
self.assertEqual(sorted_list_difference([2], [1, 1]), ([2], [1]))
2727
self.assertEqual(sorted_list_difference([1, 2], [1, 1]), ([2], []))
2828

29+
def test_sorted_list_difference_tail_deduplication(self):
30+
# Tail deduplication when one list is exhausted before the other.
31+
# These exercise the except-IndexError path in sorted_list_difference.
32+
self.assertEqual(sorted_list_difference([], [0, 0]), ([], [0]))
33+
self.assertEqual(sorted_list_difference([0, 0], []), ([0], []))
34+
self.assertEqual(sorted_list_difference([], [1, 1, 2, 2]), ([], [1, 2]))
35+
self.assertEqual(sorted_list_difference([1, 1, 2, 2], []), ([1, 2], []))
36+
# One list exhausts mid-way, leaving duplicated tail in the other.
37+
self.assertEqual(sorted_list_difference([1], [1, 2, 2, 3, 3]), ([], [2, 3]))
38+
self.assertEqual(sorted_list_difference([1, 2, 2, 3, 3], [1]), ([2, 3], []))
39+
40+
def test_sorted_list_difference_strings(self):
41+
self.assertEqual(
42+
sorted_list_difference(['a', 'b'], ['b', 'c']),
43+
(['a'], ['c']))
44+
self.assertEqual(
45+
sorted_list_difference([], ['a', 'a', 'b']),
46+
([], ['a', 'b']))
47+
self.assertEqual(
48+
sorted_list_difference(['a', 'a', 'b'], []),
49+
(['a', 'b'], []))
50+
51+
def test_sorted_list_difference_unhashable(self):
52+
self.assertEqual(
53+
sorted_list_difference([[1], [2]], [[2], [3]]),
54+
([[1]], [[3]]))
55+
self.assertEqual(
56+
sorted_list_difference([], [[0], [0]]),
57+
([], [[0]]))
58+
self.assertEqual(
59+
sorted_list_difference([[0], [0]], []),
60+
([[0]], []))
61+
2962
def test_unorderable_list_difference(self):
3063
self.assertEqual(unorderable_list_difference([], []), ([], []))
3164
self.assertEqual(unorderable_list_difference([1, 2], []), ([2, 1], []))

Lib/unittest/util.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,14 @@ def safe_repr(obj, short=False):
6363
def strclass(cls):
6464
return "%s.%s" % (cls.__module__, cls.__qualname__)
6565

66+
def _dedupe_sorted(lst):
67+
"""Remove consecutive duplicate elements from a sorted list."""
68+
result = []
69+
for item in lst:
70+
if not result or result[-1] != item:
71+
result.append(item)
72+
return result
73+
6674
def sorted_list_difference(expected, actual):
6775
"""Finds elements in only one or the other of two, sorted input lists.
6876
@@ -98,8 +106,8 @@ def sorted_list_difference(expected, actual):
98106
while actual[j] == a:
99107
j += 1
100108
except IndexError:
101-
missing.extend(expected[i:])
102-
unexpected.extend(actual[j:])
109+
missing.extend(_dedupe_sorted(expected[i:]))
110+
unexpected.extend(_dedupe_sorted(actual[j:]))
103111
break
104112
return missing, unexpected
105113

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix ``unittest.util.sorted_list_difference()`` to deduplicate remaining
2+
elements when one input list is exhausted before the other.

0 commit comments

Comments
 (0)