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