From 97f8b328bedd4b18cb09ea4a624eb760e1918b23 Mon Sep 17 00:00:00 2001 From: Maciej <1554276+zero323@users.noreply.github.com> Date: Sat, 9 Oct 2021 13:19:38 +0200 Subject: [PATCH] Modify sorted_by_file_and_line to sort only by (filename, line) (#67) Currently, sorted_by_file_and_line uses `(filename, line, content)`. This means it is not stable within `(filename, line)` and can incorrectly reorder multiline outputs Closes #66 --- pytest_mypy_plugins/tests/test_utils.py | 33 +++++++++++++++++++++++++ pytest_mypy_plugins/utils.py | 10 ++++---- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/pytest_mypy_plugins/tests/test_utils.py b/pytest_mypy_plugins/tests/test_utils.py index 345737c..20d7a66 100644 --- a/pytest_mypy_plugins/tests/test_utils.py +++ b/pytest_mypy_plugins/tests/test_utils.py @@ -9,6 +9,7 @@ TypecheckAssertionError, assert_expected_matched_actual, extract_output_matchers_from_comments, + sorted_by_file_and_line, ) @@ -184,3 +185,35 @@ def test_assert_expected_matched_actual_failures( assert_expected_matched_actual(expected, actual_lines) assert e.value.error_message.strip() == expected_error_message.strip() + + +@pytest.mark.parametrize( + "input_lines", + [ + [ + '''main:12: error: No overload variant of "f" matches argument type "List[int]"''', + """main:12: note: Possible overload variants:""", + """main:12: note: def f(x: int) -> int""", + """main:12: note: def f(x: str) -> str""", + ], + [ + '''main_a:12: error: No overload variant of "g" matches argument type "List[int]"''', + '''main_b:12: error: No overload variant of "f" matches argument type "List[int]"''', + """main_b:12: note: Possible overload variants:""", + """main_a:12: note: def g(b: int) -> int""", + """main_a:12: note: def g(a: int) -> int""", + """main_b:12: note: def f(x: int) -> int""", + """main_b:12: note: def f(x: str) -> str""", + ], + ], +) +def test_sorted_by_file_and_line_is_stable(input_lines: List[str]) -> None: + def lines_for_file(lines: List[str], fname: str) -> List[str]: + prefix = f"{fname}:" + return [line for line in lines if line.startswith(prefix)] + + files = sorted({line.split(":", maxsplit=1)[0] for line in input_lines}) + sorted_lines = sorted_by_file_and_line(input_lines) + + for f in files: + assert lines_for_file(sorted_lines, f) == lines_for_file(input_lines, f) diff --git a/pytest_mypy_plugins/utils.py b/pytest_mypy_plugins/utils.py index ff6fcee..1811a77 100644 --- a/pytest_mypy_plugins/utils.py +++ b/pytest_mypy_plugins/utils.py @@ -194,15 +194,15 @@ def remove_empty_lines(lines: List[str]) -> List[str]: def sorted_by_file_and_line(lines: List[str]) -> List[str]: - def extract_parts_as_tuple(line: str) -> Tuple[str, int, str]: + def extract_parts_as_tuple(line: str) -> Tuple[str, int]: if len(line.split(":", maxsplit=2)) < 3: - return "", 0, "" + return "", 0 - fname, line_number, contents = line.split(":", maxsplit=2) + fname, line_number, _ = line.split(":", maxsplit=2) try: - return fname, int(line_number), contents + return fname, int(line_number) except ValueError: - return "", 0, "" + return "", 0 return sorted(lines, key=extract_parts_as_tuple)