Repository: Textualize/rich
File: tests/test_cells.py (Python)

Code before:


@pytest.mark.parametrize(
    "text,expected_spans,expected_cell_length",
    [
        ("", [], 0),
        ("a", [(0, 1, 1)], 1),
        ("ab", [(0, 1, 1), (1, 2, 1)], 2),
        ("💩", [(0, 1, 2)], 2),
        ("わさび", [(0, 1, 2), (1, 2, 2), (2, 3, 2)], 6),
        (
            "👩\u200d🔧",
            [(0, 3, 2)],
            2,
        ),  # 3 code points for female mechanic: female, joiner, spanner
        ("a👩\u200d🔧", [(0, 1, 1), (1, 4, 2)], 3),
        ("a👩\u200d🔧b", [(0, 1, 1), (1, 4, 2), (4, 5, 1)], 4),
        ("⬇", [(0, 1, 1)], 1),
        ("⬇️", [(0, 2, 2)], 2),  # Variation selector, makes it double width
        ("♻", [(0, 1, 1)], 1),
        ("♻️", [(0, 2, 2)], 2),
        ("♻♻️", [(0, 1, 1), (1, 3, 2)], 3),
        ("\x1b", [(0, 1, 0)], 0),  # One escape sahould have zero width
        ("\x1b\x1b", [(0, 2, 0)], 0),  # Two escapes should have zero width
        (
            "\ufe0f",
            [(0, 1, 0)],
            0,
        ),  # Variation selector 16, without anything to change should have zero width
        (
            "\ufe0f\ufe0f",
            [(0, 2, 0)],
            0,
        ),  # 2 X variation selector 16, without anything to change should have zero width
        (
            "\u200d",
            [(0, 1, 0)],
            0,
        ),  # A zero width joiner within noting prior should have zero width
        (
            "\u200d\u200d",
            [(0, 2, 0)],
            0,
        ),  # Two ZWJs should have zero width
    ],
)
def test_split_graphemes(
    text: str, expected_spans: list[CellSpan], expected_cell_length: int
):
    spans, cell_length = split_graphemes(text)
    print(spans)

Review comment (bug):
Add a regression case where VS16 follows a zero-width character that doesn't set `last_measured_character` (e.g. "\x1b\ufe0f" or "\u200d\ufe0f"). This currently can trigger the same non-advancing-index infinite loop in `split_graphemes`, and a test here would prevent it from returning.

Code after:


@pytest.mark.parametrize(
    "text,expected_spans,expected_cell_length",
    [
        ("", [], 0),
        ("a", [(0, 1, 1)], 1),
        ("ab", [(0, 1, 1), (1, 2, 1)], 2),
        ("💩", [(0, 1, 2)], 2),
        ("わさび", [(0, 1, 2), (1, 2, 2), (2, 3, 2)], 6),
        (
            "👩\u200d🔧",
            [(0, 3, 2)],
            2,
        ),  # 3 code points for female mechanic: female, joiner, spanner
        ("a👩\u200d🔧", [(0, 1, 1), (1, 4, 2)], 3),
        ("a👩\u200d🔧b", [(0, 1, 1), (1, 4, 2), (4, 5, 1)], 4),
        ("⬇", [(0, 1, 1)], 1),
        ("⬇️", [(0, 2, 2)], 2),  # Variation selector, makes it double width
        ("♻", [(0, 1, 1)], 1),
        ("♻️", [(0, 2, 2)], 2),
        ("♻♻️", [(0, 1, 1), (1, 3, 2)], 3),
        ("\x1b", [(0, 1, 0)], 0),  # One escape should have zero width
        ("\x1b\x1b", [(0, 2, 0)], 0),  # Two escapes should have zero width
        (
            "\ufe0f",
            [(0, 1, 0)],
            0,
        ),  # Variation selector 16, without anything to change should have zero width
        (
            "\ufe0f\ufe0f",
            [(0, 2, 0)],
            0,
        ),  # 2 X variation selector 16, without anything to change should have zero width
        (
            "\u200d",
            [(0, 1, 0)],
            0,
        ),  # A zero width joiner within noting prior should have zero width
        (
            "\u200d\u200d",
            [(0, 2, 0)],
            0,
        ),  # Two ZWJs should have zero width
    ],
)
def test_split_graphemes(
    text: str, expected_spans: list[CellSpan], expected_cell_length: int
):
    spans, cell_length = split_graphemes(text)
    print(spans)