From fb274f11e22534164e55415db712ceeff1d44116 Mon Sep 17 00:00:00 2001 From: Michael Blahay Date: Thu, 27 Apr 2023 11:54:45 -0600 Subject: [PATCH 1/6] Correcting message display issue causing output of assertMultiLineEqual to be garbled. --- Lib/test/test_unittest/test_assertions.py | 6 +-- Lib/test/test_unittest/test_case.py | 60 +++++++++++++++++++++++ Lib/unittest/case.py | 36 ++++++++++---- 3 files changed, 90 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_unittest/test_assertions.py b/Lib/test/test_unittest/test_assertions.py index 6557104b81fc0f..5c1a28ecda5b49 100644 --- a/Lib/test/test_unittest/test_assertions.py +++ b/Lib/test/test_unittest/test_assertions.py @@ -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), diff --git a/Lib/test/test_unittest/test_case.py b/Lib/test/test_unittest/test_case.py index dd5ff6d553e61d..ed5eb5609a5dd1 100644 --- a/Lib/test/test_unittest/test_case.py +++ b/Lib/test/test_unittest/test_case.py @@ -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(): diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index 018f22e7ce0c73..c3fd661c9a4ca5 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -1216,20 +1216,38 @@ def assertCountEqual(self, first, second, msg=None): self.fail(msg) 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') + """Assert that two multi-line strings are equal. + If the assertion fails, then provide an error message with a detailed + diff output + """ + 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 first == '' and second != '' and second[-1] != '\n': + second_presplit += '\n' + elif second == '' and 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) From 58f7f6223b788fc5cfb57991870ac1df23837ac0 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 27 Apr 2023 18:46:32 +0000 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Library/2023-04-27-18-46-31.gh-issue-68968.E3tnhy.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2023-04-27-18-46-31.gh-issue-68968.E3tnhy.rst diff --git a/Misc/NEWS.d/next/Library/2023-04-27-18-46-31.gh-issue-68968.E3tnhy.rst b/Misc/NEWS.d/next/Library/2023-04-27-18-46-31.gh-issue-68968.E3tnhy.rst new file mode 100644 index 00000000000000..a3ae55c32b408e --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-04-27-18-46-31.gh-issue-68968.E3tnhy.rst @@ -0,0 +1 @@ +Correcting message display issue causing output of assertEqual to be garbled when provided string inputs. From 046dcb4f4e3acddcdcdf8b11449c5c27a023588c Mon Sep 17 00:00:00 2001 From: Michael Blahay Date: Tue, 2 May 2023 20:53:38 -0600 Subject: [PATCH 3/6] Update Lib/unittest/case.py Adding period missing in doc string for assertMultiLineEqual. Co-authored-by: Carl Meyer --- Lib/unittest/case.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index c3fd661c9a4ca5..bcc1ffde3ae2f5 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -1217,8 +1217,9 @@ def assertCountEqual(self, first, second, msg=None): def assertMultiLineEqual(self, first, second, msg=None): """Assert that two multi-line strings are equal. - If the assertion fails, then provide an error message with a detailed - diff output + + If the assertion fails, provide an error message with a detailed + diff output. """ self.assertIsInstance(first, str, "First argument is not a string") self.assertIsInstance(second, str, "Second argument is not a string") From cc191c77056fc93d1e880e4ee65f9c5e7f66e811 Mon Sep 17 00:00:00 2001 From: Michael Blahay Date: Tue, 2 May 2023 21:09:59 -0600 Subject: [PATCH 4/6] Update Misc/NEWS.d/next/Library/2023-04-27-18-46-31.gh-issue-68968.E3tnhy.rst Modifying news entry to be more precise Co-authored-by: Carl Meyer --- .../next/Library/2023-04-27-18-46-31.gh-issue-68968.E3tnhy.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Library/2023-04-27-18-46-31.gh-issue-68968.E3tnhy.rst b/Misc/NEWS.d/next/Library/2023-04-27-18-46-31.gh-issue-68968.E3tnhy.rst index a3ae55c32b408e..bf29b64793b933 100644 --- a/Misc/NEWS.d/next/Library/2023-04-27-18-46-31.gh-issue-68968.E3tnhy.rst +++ b/Misc/NEWS.d/next/Library/2023-04-27-18-46-31.gh-issue-68968.E3tnhy.rst @@ -1 +1 @@ -Correcting message display issue causing output of assertEqual to be garbled when provided string inputs. +Fixed garbled output of :meth:`~unittest.TestCase.assertEqual` when an input lacks final newline. From 1990b4de09688ba4d8decc9d53223bc9ab3a93f0 Mon Sep 17 00:00:00 2001 From: Michael Blahay Date: Wed, 3 May 2023 18:21:49 -0600 Subject: [PATCH 5/6] Update Lib/unittest/case.py Per carljm suggestion, removing unnecessary conditions. Co-authored-by: Carl Meyer --- Lib/unittest/case.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index bcc1ffde3ae2f5..116670bbd7fab1 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -1236,13 +1236,13 @@ def assertMultiLineEqual(self, first, second, msg=None): # \n should be added first_presplit = first second_presplit = second - if first != '' and second != '': + if first and second: if first[-1] != '\n' or second[-1] != '\n': first_presplit += '\n' second_presplit += '\n' - elif first == '' and second != '' and second[-1] != '\n': + elif second and second[-1] != '\n': second_presplit += '\n' - elif second == '' and first != '' and first[-1] != '\n': + elif first and first[-1] != '\n': first_presplit += '\n' firstlines = first_presplit.splitlines(keepends=True) From 914e920f03dd29e97d4e2d8f648e1dff51624d53 Mon Sep 17 00:00:00 2001 From: Michael Blahay Date: Wed, 3 May 2023 18:35:47 -0600 Subject: [PATCH 6/6] Update case.py Reverting back to single line doc string for assertMultiLineEqual. --- Lib/unittest/case.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Lib/unittest/case.py b/Lib/unittest/case.py index 116670bbd7fab1..001b640dc43ad6 100644 --- a/Lib/unittest/case.py +++ b/Lib/unittest/case.py @@ -1216,11 +1216,7 @@ def assertCountEqual(self, first, second, msg=None): self.fail(msg) def assertMultiLineEqual(self, first, second, msg=None): - """Assert that two multi-line strings are equal. - - If the assertion fails, provide an error message with a detailed - diff output. - """ + """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")