diff --git a/AUTHORS b/AUTHORS index cc53ce10d..4190da68c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -192,6 +192,7 @@ Jake VanderPlas Jakob van Santen Jakub Mitoraj James Bourbeau +Jamie Chen Jan Balster Janne Vanhala Jason R. Coombs @@ -218,6 +219,7 @@ Justice Ndou Justyna Janczyszyn Kale Kundert Kamran Ahmad +Kaylin Yeoh Kenny Y Karl O. Pinc Karthikeyan Singaravelan diff --git a/changelog/10863.bugfix.rst b/changelog/10863.bugfix.rst new file mode 100644 index 000000000..60ac21559 --- /dev/null +++ b/changelog/10863.bugfix.rst @@ -0,0 +1,4 @@ +Found source causes multiline string diff error +Added private functions to check specific cases +Fixed output issue with implicit concatonated strings +Added tests in test assertion file in function test_multiline_diff diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index a118befcc..60f359571 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -14,6 +14,7 @@ from typing import Mapping from typing import Optional from typing import Protocol from typing import Sequence +from typing import Tuple from unicodedata import normalize from _pytest import outcomes @@ -333,8 +334,17 @@ def _compare_eq_iterable( # dynamic import to speedup pytest import difflib - left_formatting = PrettyPrinter().pformat(left).splitlines() - right_formatting = PrettyPrinter().pformat(right).splitlines() + if _is_empty_vs_non_empty(left, right): + left_formatting, right_formatting = _format_for_empty_and_non_empty(left, right) + lines_left = len(left_formatting) + lines_right = len(right_formatting) + + if lines_left > 1 or lines_right > 1: + _surrounding_parens_on_own_lines(left_formatting) + _surrounding_parens_on_own_lines(right_formatting) + else: + left_formatting = PrettyPrinter().pformat(left).splitlines() + right_formatting = PrettyPrinter().pformat(right).splitlines() explanation = ["", "Full diff:"] # "right" is the expected base against which we compare "left", @@ -351,6 +361,43 @@ def _compare_eq_iterable( return explanation +def _surrounding_parens_on_own_lines(lines: List[str]) -> None: + """Move opening/closing parenthesis/bracket to own lines.""" + opening = lines[0][:1] + if opening in ["(", "[", "{"]: + lines[0] = " " + lines[0][1:] + lines[:] = [opening] + lines + closing = lines[-1][-1:] + if closing in [")", "]", "}"]: + lines[-1] = lines[-1][:-1] + "," + lines[:] = lines + [closing] + + +def _is_empty_vs_non_empty(left: Iterable[Any], right: Iterable[Any]) -> bool: + is_left_empty = not any(left) if isinstance(left, Iterable) else not left + is_right_empty = not any(right) if isinstance(right, Iterable) else not right + return (is_left_empty and not is_right_empty) or ( + not is_left_empty and is_right_empty + ) + + +def _format_for_empty_and_non_empty( + left: Iterable[Any], right: Iterable[Any] +) -> Tuple[List[str], List[str]]: + if isinstance(left, (list, tuple)) and isinstance(right, (list, tuple)): + if not left: + right_width = max(len(s) + 4 for s in right) + right_formatting = pprint.pformat(right, width=right_width).splitlines() + left_formatting = pprint.pformat(left).splitlines() + else: + left_width = max(len(s) + 4 for s in left) + left_formatting = pprint.pformat(left, width=left_width).splitlines() + right_formatting = pprint.pformat(right).splitlines() + return left_formatting, right_formatting + else: + return pprint.pformat(left).splitlines(), pprint.pformat(right).splitlines() + + def _compare_eq_sequence( left: Sequence[Any], right: Sequence[Any], diff --git a/testing/test_assertion.py b/testing/test_assertion.py index a8960436b..af0c3e9f4 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -396,6 +396,89 @@ class TestAssert_reprcompare: "+ spam", ] + def test_multiline_diff(self) -> None: + m1 = [ + "This is some dummy test which shows the strange way in which Pycharm" + " displays the full diff." + ] + m2 = [ + "This is some dummy test which shows the strange way in which Pycharm" + " displays the full diff.This is some dummy test which shows the strange way in which Pycharm" + " displays the full diff." + ] + m3 = [ + "This is some dummy test which shows the strange way in which Pycharm" + " displays the full diff.", + "This is some dummy test which shows the strange way in which Pycharm" + " displays the full diff.", + ] + + assert callequal(m1, []) == [ + "['This is som...e full diff.'] == []", + "", + "Left contains one more item: 'This is some dummy test which shows the " + "strange way in which Pycharm displays the full diff.'", + "Use -v to get more diff", + ] + assert callequal(m1, [], verbose=True) == [ + "['This is som...e full diff.'] == []", + "", + "Left contains one more item: 'This is some dummy test which shows the " + "strange way in which Pycharm displays the full diff.'", + "", + "Full diff:", + "- []", + "+ ['This is some dummy test which shows the strange way in which Pycharm " + "displays the full diff.']", + ] + + assert callequal(m2, []) == [ + "['This is som...e full diff.'] == []", + "", + "Left contains one more item: 'This is some dummy test which shows the " + "strange way in which Pycharm displays the full diff.This is some dummy test " + "which shows the strange way in which Pycharm displays the full diff.'", + "Use -v to get more diff", + ] + + assert callequal(m2, [], verbose=True) == [ + "['This is som...e full diff.'] == []", + "", + "Left contains one more item: 'This is some dummy test which shows the " + "strange way in which Pycharm displays the full diff.This is some dummy test " + "which shows the strange way in which Pycharm displays the full diff.'", + "" "", + "Full diff:", + "- []", + "+ ['This is some dummy test which shows the strange way in which Pycharm " + "displays the full diff.This is some dummy test which shows the strange " + "way in which Pycharm displays the full diff.']", + ] + + assert callequal(m3, []) == [ + "['This is som...e full diff.'] == []", + "", + "Left contains 2 more items, first extra item: 'This is some dummy test which shows the strange way in " + "which Pycharm displays the full diff.'", + "" "Use -v to get more diff", + ] + + assert callequal(m3, [], verbose=True) == [ + "['This is som...e full diff.'] == []", + "", + "Left contains 2 more items, first extra item: 'This is some dummy test which shows the strange way in " + "which Pycharm displays the full diff.'", + "", + "Full diff:", + " [", + "- ,", + "+ 'This is some dummy test which shows the strange way in which Pycharm " + "displays the full diff.',", + "+ 'This is some dummy test which shows the strange way in which Pycharm " + "displays the full diff.',", + " ]", + ] + def test_text_skipping(self) -> None: lines = callequal("a" * 50 + "spam", "a" * 50 + "eggs") assert lines is not None