diff --git a/AUTHORS b/AUTHORS index 1ee868448d4..e6da51f6e12 100644 --- a/AUTHORS +++ b/AUTHORS @@ -103,6 +103,7 @@ CrazyMerlyn Cristian Vera Cyrus Maden Damian Skrzypczak +Daniel Diniz Daniel Grana Daniel Hahler Daniel Miller diff --git a/changelog/12406.improvement.rst b/changelog/12406.improvement.rst new file mode 100644 index 00000000000..c082193e524 --- /dev/null +++ b/changelog/12406.improvement.rst @@ -0,0 +1,2 @@ +The ``_diff_text()`` function now truncates texts before diffing them +if not in verbose mode. -- by :user:`devdanzin`. diff --git a/src/_pytest/assertion/util.py b/src/_pytest/assertion/util.py index 4dc1af4af03..2199c54024a 100644 --- a/src/_pytest/assertion/util.py +++ b/src/_pytest/assertion/util.py @@ -21,6 +21,8 @@ from _pytest._io.pprint import PrettyPrinter from _pytest._io.saferepr import saferepr from _pytest._io.saferepr import saferepr_unlimited +from _pytest.assertion.truncate import DEFAULT_MAX_CHARS +from _pytest.assertion.truncate import DEFAULT_MAX_LINES from _pytest.config import Config @@ -285,17 +287,18 @@ def _diff_text(left: str, right: str, verbose: int = 0) -> list[str]: explanation: list[str] = [] if verbose < 1: - i = 0 # just in case left or right has zero length - for i in range(min(len(left), len(right))): - if left[i] != right[i]: + leading_skipped = 0 # just in case left or right has zero length + for leading_skipped in range(min(len(left), len(right))): + if left[leading_skipped] != right[leading_skipped]: break - if i > 42: - i -= 10 # Provide some context + if leading_skipped > 42: + leading_skipped -= 10 # Provide some context explanation = [ - f"Skipping {i} identical leading characters in diff, use -v to show" + f"Skipping {leading_skipped} identical leading characters in diff, use -v to show" ] - left = left[i:] - right = right[i:] + left = left[leading_skipped:] + right = right[leading_skipped:] + i = 0 if len(left) == len(right): for i in range(len(left)): if left[-i] != right[-i]: @@ -308,6 +311,21 @@ def _diff_text(left: str, right: str, verbose: int = 0) -> list[str]: ] left = left[:-i] right = right[:-i] + shortest = min(left, right, key=lambda x: len(x)) + lines = j = start = 0 + if shortest.count("\n") >= DEFAULT_MAX_LINES: + # Keep only DEFAULT_MAX_LINES, usually 8, lines + if 10 < leading_skipped < 42: # We didn't skip equal leading characters + start += leading_skipped - 10 + for j, c in enumerate(shortest[start:], start=start - 1): + if c == "\n": + lines += 1 + if lines > DEFAULT_MAX_LINES: + break + else: + j = len(max(left, right, key=lambda x: len(x))) + left = left[start : min(DEFAULT_MAX_CHARS, len(left), j)] + right = right[start : min(DEFAULT_MAX_CHARS, len(right), j)] keepends = True if left.isspace() or right.isspace(): left = repr(str(left)) diff --git a/testing/test_assertion.py b/testing/test_assertion.py index b10ca1c91f4..51647babce6 100644 --- a/testing/test_assertion.py +++ b/testing/test_assertion.py @@ -417,6 +417,42 @@ def test_multiline_text_diff(self) -> None: assert "- eggs" in diff assert "+ spam" in diff + def test_long_text_diff_same_trailing(self) -> None: + left = "foo\nbar\n" * 100 + "spam\n" * 100 + "spam\n" * 160 + right = "foo\nbar\n" * 100 + "eggs\n" * 100 + "spam\n" * 160 + diff = callequal(left, right, verbose=0) + assert diff is not None + assert "- eggs" in diff + assert len(diff) == 19 + + def test_long_text_diff_different_trailing(self) -> None: + left = "foo\nbar\n" * 100 + "spam\n" * 100 + "spam\n" * 160 + right = "foo\nbar\n" * 100 + "eggs\n" * 100 + "eggs\n" * 160 + diff = callequal(left, right, verbose=0) + assert diff is not None + assert "- eggs" in diff + assert len(diff) == 18 + + def test_long_text_diff_short_trailing(self) -> None: + left = "foo\nbar\n" * 5 + "spam\n" * 5 + "spam\n" * 5 + right = "foo\nbar\n" * 5 + "eggs\n" * 5 + "spam\n" * 5 + diff = callequal(left, right, verbose=0) + assert diff is not None + assert "- eggs" in diff + assert len(diff) == 16 + + def test_long_text_diff_long_lines(self) -> None: + long_line1 = "foo" * 50 + "\n" + long_line2 = "bar" * 50 + "\n" + left = long_line1 * 3 + "spam\n" * 3 + long_line1 * 10 + right = long_line1 * 3 + "eggs\n" * 3 + long_line2 * 10 + diff = callequal(left, right, verbose=0) + assert diff is not None + assert "- eggs" in diff + assert "+ " + long_line1[:-1] in diff + assert "- " + long_line2[:-1] in diff + assert len(diff) == 20 + def test_bytes_diff_normal(self) -> None: """Check special handling for bytes diff (#5260)""" diff = callequal(b"spam", b"eggs")