From 946434c610430d42cd2434d62bedd1f8dce45256 Mon Sep 17 00:00:00 2001 From: Daniel Hahler Date: Mon, 7 Oct 2019 02:25:18 +0200 Subject: [PATCH] Improve full diff output for lists Massage text input for difflib when comparing pformat output of different line lengths. Also do not strip ndiff output on the left, which currently already removes indenting for lines with no differences. Before: E AssertionError: assert ['version', '...version_info'] == ['version', '...version', ...] E Right contains 3 more items, first extra item: ' ' E Full diff: E - ['version', 'version_info', 'sys.version', 'sys.version_info'] E + ['version', E + 'version_info', E + 'sys.version', E + 'sys.version_info', E + ' ', E + 'sys.version', E + 'sys.version_info'] After: E AssertionError: assert ['version', '...version_info'] == ['version', '...version', ...] E Right contains 3 more items, first extra item: ' ' E Full diff: E [ E 'version', E 'version_info', E 'sys.version', E 'sys.version_info', E + ' ', E + 'sys.version', E + 'sys.version_info', E ] --- changelog/5924.feature.rst | 34 ++++++++++++++++++++++++ src/_pytest/assertion/util.py | 32 ++++++++++++++++++++++- testing/test_assertion.py | 49 +++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 changelog/5924.feature.rst diff --git a/changelog/5924.feature.rst b/changelog/5924.feature.rst new file mode 100644 index 000000000..a03eb4704 --- /dev/null +++ b/changelog/5924.feature.rst @@ -0,0 +1,34 @@ +Improve verbose diff output with sequences. + +Before: + +.. code-block:: + + E AssertionError: assert ['version', '...version_info'] == ['version', '...version', ...] + E Right contains 3 more items, first extra item: ' ' + E Full diff: + E - ['version', 'version_info', 'sys.version', 'sys.version_info'] + E + ['version', + E + 'version_info', + E + 'sys.version', + E + 'sys.version_info', + E + ' ', + E + 'sys.version', + E + 'sys.version_info'] + +After: + +.. code-block:: + + E AssertionError: assert ['version', '...version_info'] == ['version', '...version', ...] + E Right contains 3 more items, first extra item: ' ' + E Full diff: + E [ + E 'version', + E 'version_info', + E 'sys.version', + E 'sys.version_info', + E + ' ', + E + 'sys.version', + E + 'sys.version_info', + E ] diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index c2a4e446f..0350b0b07 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -246,6 +246,18 @@ def _compare_eq_verbose(left, right): return explanation +def _surrounding_parens_on_own_lines(lines): # type: (List) -> 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 _compare_eq_iterable(left, right, verbose=0): if not verbose: return ["Use -v to get the full diff"] @@ -254,9 +266,27 @@ def _compare_eq_iterable(left, right, verbose=0): left_formatting = pprint.pformat(left).splitlines() right_formatting = pprint.pformat(right).splitlines() + + # Re-format for different output lengths. + lines_left = len(left_formatting) + lines_right = len(right_formatting) + if lines_left != lines_right: + if lines_left > lines_right: + max_width = min(len(x) for x in left_formatting) + right_formatting = pprint.pformat(right, width=max_width).splitlines() + lines_right = len(right_formatting) + else: + max_width = min(len(x) for x in right_formatting) + left_formatting = pprint.pformat(left, width=max_width).splitlines() + lines_left = len(left_formatting) + + if lines_left > 1 or lines_right > 1: + _surrounding_parens_on_own_lines(left_formatting) + _surrounding_parens_on_own_lines(right_formatting) + explanation = ["Full diff:"] explanation.extend( - line.strip() for line in difflib.ndiff(left_formatting, right_formatting) + line.rstrip() for line in difflib.ndiff(left_formatting, right_formatting) ) return explanation diff --git a/testing/test_assertion.py b/testing/test_assertion.py index 56729d28a..999f64a0e 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -413,6 +413,55 @@ class TestAssert_reprcompare: expl = callequal([0, 1, 2], [0, 1]) assert len(expl) > 1 + def test_list_wrap_for_multiple_lines(self): + long_d = "d" * 80 + l1 = ["a", "b", "c"] + l2 = ["a", "b", "c", long_d] + diff = callequal(l1, l2, verbose=True) + assert diff == [ + "['a', 'b', 'c'] == ['a', 'b', 'c...dddddddddddd']", + "Right contains one more item: '" + long_d + "'", + "Full diff:", + " [", + " 'a',", + " 'b',", + " 'c',", + "+ '" + long_d + "',", + " ]", + ] + + diff = callequal(l2, l1, verbose=True) + assert diff == [ + "['a', 'b', 'c...dddddddddddd'] == ['a', 'b', 'c']", + "Left contains one more item: '" + long_d + "'", + "Full diff:", + " [", + " 'a',", + " 'b',", + " 'c',", + "- '" + long_d + "',", + " ]", + ] + + def test_list_wrap_for_width_rewrap_same_length(self): + long_a = "a" * 30 + long_b = "b" * 30 + long_c = "c" * 30 + l1 = [long_a, long_b, long_c] + l2 = [long_b, long_c, long_a] + diff = callequal(l1, l2, verbose=True) + assert diff == [ + "['aaaaaaaaaaa...cccccccccccc'] == ['bbbbbbbbbbb...aaaaaaaaaaaa']", + "At index 0 diff: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' != 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'", + "Full diff:", + " [", + "- 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',", + " 'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',", + " 'cccccccccccccccccccccccccccccc',", + "+ 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',", + " ]", + ] + def test_dict(self): expl = callequal({"a": 0}, {"a": 1}) assert len(expl) > 1