Skip to content

Commit

Permalink
gh-68968: Correcting message display issue with assertEqual (#103937)
Browse files Browse the repository at this point in the history
  • Loading branch information
mblahay committed May 4, 2023
1 parent 7d35c31 commit 46361bb
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 11 deletions.
6 changes: 3 additions & 3 deletions Lib/test/test_unittest/test_assertions.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,9 @@ def testAssertDictEqual(self):

def testAssertMultiLineEqual(self):
self.assertMessages('assertMultiLineEqual', ("", "foo"),
[r"\+ foo$", "^oops$",
r"\+ foo$",
r"\+ foo : oops$"])
[r"\+ foo\n$", "^oops$",
r"\+ foo\n$",
r"\+ foo\n : oops$"])

def testAssertLess(self):
self.assertMessages('assertLess', (2, 1),
Expand Down
60 changes: 60 additions & 0 deletions Lib/test/test_unittest/test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -1149,6 +1149,66 @@ def testAssertEqualSingleLine(self):
error = str(e).split('\n', 1)[1]
self.assertEqual(sample_text_error, error)

def testAssertEqualwithEmptyString(self):
'''Verify when there is an empty string involved, the diff output
does not treat the empty string as a single empty line. It should
instead be handled as a non-line.
'''
sample_text = ''
revised_sample_text = 'unladen swallows fly quickly'
sample_text_error = '''\
+ unladen swallows fly quickly
'''
try:
self.assertEqual(sample_text, revised_sample_text)
except self.failureException as e:
# need to remove the first line of the error message
error = str(e).split('\n', 1)[1]
self.assertEqual(sample_text_error, error)

def testAssertEqualMultipleLinesMissingNewlineTerminator(self):
'''Verifying format of diff output from assertEqual involving strings
with multiple lines, but missing the terminating newline on both.
'''
sample_text = 'laden swallows\nfly sloely'
revised_sample_text = 'laden swallows\nfly slowly'
sample_text_error = '''\
laden swallows
- fly sloely
? ^
+ fly slowly
? ^
'''
try:
self.assertEqual(sample_text, revised_sample_text)
except self.failureException as e:
# need to remove the first line of the error message
error = str(e).split('\n', 1)[1]
self.assertEqual(sample_text_error, error)

def testAssertEqualMultipleLinesMismatchedNewlinesTerminators(self):
'''Verifying format of diff output from assertEqual involving strings
with multiple lines and mismatched newlines. The output should
include a - on it's own line to indicate the newline difference
between the two strings
'''
sample_text = 'laden swallows\nfly sloely\n'
revised_sample_text = 'laden swallows\nfly slowly'
sample_text_error = '''\
laden swallows
- fly sloely
? ^
+ fly slowly
? ^
-\x20
'''
try:
self.assertEqual(sample_text, revised_sample_text)
except self.failureException as e:
# need to remove the first line of the error message
error = str(e).split('\n', 1)[1]
self.assertEqual(sample_text_error, error)

def testEqualityBytesWarning(self):
if sys.flags.bytes_warning:
def bytes_warning():
Expand Down
31 changes: 23 additions & 8 deletions Lib/unittest/case.py
Original file line number Diff line number Diff line change
Expand Up @@ -1217,19 +1217,34 @@ def assertCountEqual(self, first, second, msg=None):

def assertMultiLineEqual(self, first, second, msg=None):
"""Assert that two multi-line strings are equal."""
self.assertIsInstance(first, str, 'First argument is not a string')
self.assertIsInstance(second, str, 'Second argument is not a string')
self.assertIsInstance(first, str, "First argument is not a string")
self.assertIsInstance(second, str, "Second argument is not a string")

if first != second:
# don't use difflib if the strings are too long
# Don't use difflib if the strings are too long
if (len(first) > self._diffThreshold or
len(second) > self._diffThreshold):
self._baseAssertEqual(first, second, msg)
firstlines = first.splitlines(keepends=True)
secondlines = second.splitlines(keepends=True)
if len(firstlines) == 1 and first.strip('\r\n') == first:
firstlines = [first + '\n']
secondlines = [second + '\n']

# Append \n to both strings if either is missing the \n.
# This allows the final ndiff to show the \n difference. The
# exception here is if the string is empty, in which case no
# \n should be added
first_presplit = first
second_presplit = second
if first and second:
if first[-1] != '\n' or second[-1] != '\n':
first_presplit += '\n'
second_presplit += '\n'
elif second and second[-1] != '\n':
second_presplit += '\n'
elif first and first[-1] != '\n':
first_presplit += '\n'

firstlines = first_presplit.splitlines(keepends=True)
secondlines = second_presplit.splitlines(keepends=True)

# Generate the message and diff, then raise the exception
standardMsg = '%s != %s' % _common_shorten_repr(first, second)
diff = '\n' + ''.join(difflib.ndiff(firstlines, secondlines))
standardMsg = self._truncateMessage(standardMsg, diff)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fixed garbled output of :meth:`~unittest.TestCase.assertEqual` when an input lacks final newline.

0 comments on commit 46361bb

Please sign in to comment.