Files
MeshTerm/tests/test_pathline.py
Jean-Pierre MartineauandClaude Opus 5.5 1b30f353c6 A cut in the text beside a chip ends on an ellipsis, not on a stray crack
When a row was a chip followed by text (a Courier entry: the recipient,
then what it waits for) and the text ran past the screen, the cut looked
back for a chip fill, found the chip far to the left, and drew a half
block in its colour at the right edge. The crack now forms only inside
the path that the row stamps, so a cut in plain text gets the ellipsis.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-22 00:22:04 -04:00

1049 lines
49 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# SPDX-License-Identifier: Apache-2.0
"""Tests for the path-line widget: the same words in both modes, and three overflow shapes.
The contract of the widget is exactness. Plain mode must reproduce the arrow presentation
of the app glyph for glyph. Powerline mode must interlock the chip fills with the
foreground and background of the separator. Each overflow shape must keep its budget of
cells. Thus these tests assert rendered strings and span styles, and not impressions.
"""
from __future__ import annotations
import random
import pytest
from rich.text import Text
import meshterm.ui.pathline as pathline
from meshterm.emulator import vt
from meshterm.emulator.vt import Style, Terminal
from meshterm.platforms import CARDPUTER_ZERO, PICOCALC_LYRA, set_platform
from meshterm.ui import oklab
from meshterm.ui.fontset import FONT_CODEPOINTS
from meshterm.ui.pathline import (
_DIM_BG,
_DIM_FG,
_YOU_BG,
CHIP_LIGHTNESS,
CRACK_HEAD,
CRACK_TAIL,
CURSOR_GLYPH,
ELIDE_HEAD,
ELIDE_TAIL,
PATH_INK,
POWERLINE_ROUND_CLOSE,
POWERLINE_ROUND_OPEN,
POWERLINE_SEP,
POWERLINE_THIN,
SEAM_BLUR,
SELF_GLYPH,
WRAP_OFFSET,
PathHop,
PathLine,
_style_hex,
cut_mark,
cut_to,
elision_hop,
hops_atom,
path_line,
with_action_mark,
)
from meshterm.ui.theme import DIM_TWIN, fold_text, node_style, slot_hex
from meshterm.ui.tui.render import render_to_ansi
from meshterm.ui.widgets import path_text
def _fill(key: str) -> str:
"""The fill of the chip of a node on a truecolor terminal: its hue, but darker."""
return pathline._chip_fill(_style_hex(node_style(key)))
def _styles(text) -> dict[str, str]: # noqa: ANN001
"""Map each styled slice of a Text to its style string, for spot checks."""
return {text.plain[s.start : s.end]: str(s.style) for s in text.spans}
def _char_styles(text) -> list[tuple[str, str]]: # noqa: ANN001
"""Each character, paired with its effective span style, for checks of exact parity.
The span *boundaries* can differ between two builders (one appends a hash in two
pieces, and the other appends it in one piece). What must be the same is the style of
each character. Thus the comparison is for each cell, and not for each span.
"""
styles = [""] * len(text.plain)
for span in text.spans:
for i in range(span.start, span.end):
styles[i] = str(span.style)
return list(zip(text.plain, styles, strict=True))
def test_plain_mode_matches_the_app_wide_arrow_presentation() -> None:
"""Plain mode reproduces the look of ``path_text`` of the app exactly.
Names have their key hue, we are white, keyless hops and arrows are muted.
"""
line = PathLine(
[
PathHop("Alice", key="aa"),
PathHop("77", key="77", lit_bytes=1),
PathHop("you", you=True),
],
mode="plain",
)
text = line.text()
assert text.plain == "Alice → 77 → you"
styles = _styles(text)
assert styles["Alice"] == node_style("aa")
assert styles["you"] == "you"
assert styles["77"] == node_style("77") # the lit hash prefix has the hue
def test_plain_mode_annotation_dim_and_custom_separator() -> None:
"""Annotations, dimming, and a caller's own separator all stay in plain mode.
The ``(3d)`` note of a trace is muted. A dim hop (and the arrow into it) fades. The own
separator of a surface passes through without a change. An example is the ``›`` of the
trail of the mesh walk.
"""
line = PathLine(
[PathHop("Hub", key="3d", annotation="3d"), PathHop("home", you=True, dim=True)],
mode="plain",
)
text = line.text()
assert text.plain == "Hub (3d) → home"
styles = _styles(text)
assert styles[" (3d)"] == "muted" # the annotation note, with its space
assert styles["home"] == "faint"
assert styles[" → "] == "faint" # the arrow into a dim hop fades with the hop
trail = PathLine([PathHop("a"), PathHop("b")], mode="plain", separator=" › ").text()
assert trail.plain == "a › b"
def test_empty_path_reads_as_the_callers_word() -> None:
"""A line with no hops is the muted empty note, in each shape."""
assert PathLine([], mode="plain").text().plain == "direct"
assert (
PathLine([], mode="powerline", empty="direct — no relays").text().plain
== "direct — no relays"
)
assert PathLine([], mode="plain").wrapped(40)[0].plain == "direct"
def test_chips_are_joined_by_one_interlocked_chevron() -> None:
"""A seam is one cell and not two, and the path's own end is not a seam at all.
The point of the previous chip is laid *on* the fill of the next chip. Thus the route
reads as a ribbon whose segments meet on a chevron. The point has a meaning: it says
that the route continues, so a finished path never has one. It ends square on the last
chip's own pad, and the only chevrons in the line are the joins.
"""
alice_fill = _fill("aa")
line = PathLine([PathHop("Alice", key="aa"), PathHop("you", you=True)], mode="powerline")
text = line.text()
assert text.plain == f" Alice {POWERLINE_SEP} you "
seam_styles = [str(s.style) for s in text.spans if text.plain[s.start : s.end] == POWERLINE_SEP]
assert seam_styles == [f"{alice_fill} on {_YOU_BG}"]
def test_chips_keep_the_same_words_and_honour_style_overrides() -> None:
"""Chips change the colours and the separators, and never the words.
An explicit style override (hex or theme name) becomes the colour of the chip. The
label has this colour, on a darker fill of it, as the hue of a node does.
"""
hops = [PathHop("Hub", key="3d", annotation="3d"), PathHop("you", you=True)]
plain = PathLine(hops, mode="plain").text().plain
chips = PathLine(hops, mode="powerline").text().plain
assert plain.replace(" → ", " ") == chips.replace(POWERLINE_SEP, "").replace(" ", " ").strip()
themed = PathLine([PathHop("X", style="brand")], mode="powerline").text()
brand = pathline._chip_fill("#5eead4")
assert pathline._fills(themed)[-1] == brand # the chip goes to the last cell
assert any(str(s.style) == f"bold #5eead4 on {brand}" for s in themed.spans)
hexed = PathLine([PathHop("X", style="bold #123456")], mode="powerline").text()
assert pathline._fills(hexed)[-1] == pathline._chip_fill("#123456")
def test_a_chip_is_the_name_colour_on_a_darker_fill_of_it() -> None:
"""The label of a node is its hue, the colour of its name on each surface.
The fill is the same hue, but darker (``CHIP_LIGHTNESS`` of its OKLab lightness). A hash
label and an annotation also have the hue, and only the lit bytes are bold. Where the
darker colour leaves the sRGB gamut, the chroma comes down and the hue stays. Thus two
greens that are a few steps apart on the wheel keep two fills.
"""
hue = _style_hex(node_style("3d"))
line = PathLine([PathHop("3d63c6", key="3d", lit_bytes=1, annotation="3d")], mode="powerline")
styles = {line.text().plain[s.start : s.end]: str(s.style) for s in line.text().spans}
assert styles["3d"] == f"bold {hue} on {_fill('3d')}"
assert styles["63c6"] == styles[" (3d)"] == f"{hue} on {_fill('3d')}"
fill = oklab.from_hex(_fill("3d"))
assert fill[0] == pytest.approx(oklab.from_hex(hue)[0] * CHIP_LIGHTNESS, abs=0.01)
assert _fill("4c") != _fill("5c") # a clip made both of them #008000
def test_auto_mode_follows_the_terminal_verdict(monkeypatch: pytest.MonkeyPatch) -> None:
"""``auto`` renders chips only when the terminal can draw them."""
hops = [PathHop("a"), PathHop("b")]
monkeypatch.setattr(pathline, "powerline_enabled", lambda: True)
assert POWERLINE_THIN in PathLine(hops).text().plain # two keyless greys give the thin seam
monkeypatch.setattr(pathline, "powerline_enabled", lambda: False)
assert PathLine(hops).text().plain == "a → b"
def test_the_cardputer_draws_chips_in_glyphs_its_font_has(monkeypatch: pytest.MonkeyPatch) -> None:
"""The own font of the Cardputer has the chevrons, so its paths are chips that fold cleanly.
MeshTerm reads its verdict from its font (:mod:`~meshterm.ui.termfont`). The render
boundary folds each glyph that the font does not have to ``?``. Thus each mark that the
chip language uses (both seams, both cracks, the elision, and our star) must come
through unchanged.
"""
monkeypatch.delenv("MESHTERM_POWERLINE")
set_platform(CARDPUTER_ZERO)
hops = [PathHop(SELF_GLYPH, you=True), PathHop("Hub", key="3d"), elision_hop()]
line = PathLine([*hops, PathHop("a"), PathHop("b")]).text() # auto: the font decides
drawn = [line, cut_to(line, 12), cut_mark(line, 1, ELIDE_HEAD)]
ink = "".join(text.plain for text in drawn)
assert {POWERLINE_SEP, POWERLINE_THIN, CRACK_TAIL, CRACK_HEAD, "⋯", SELF_GLYPH} <= set(ink)
assert fold_text(ink) == ink
def _rgb(colour: str) -> tuple[int, int, int]:
return (int(colour[1:3], 16), int(colour[3:5], 16), int(colour[5:7], 16))
def _console_cells(row: Text) -> list[tuple[str, Style]]:
"""The cells that the PicoCalc console gets for one row, read back through a VT."""
palette = tuple(_rgb(slot_hex(n)) for n in range(16))
term = Terminal(53, 1, palette=palette)
term.feed(render_to_ansi(row, 53, no_wrap=True))
return term.screen[0]
def _selected(line: Text) -> Text:
"""``line`` on a selected row, whose base style is the bold ``cursor``."""
row = Text(style="cursor")
row.append("❯ ")
row.append_text(line)
return row
@pytest.fixture
def picocalc(monkeypatch: pytest.MonkeyPatch) -> None:
"""The PicoCalc, with the chip verdict that its own font gives."""
monkeypatch.delenv("MESHTERM_POWERLINE")
set_platform(PICOCALC_LYRA)
def test_the_picocalc_draws_our_chat_chip_white_on_its_fill(picocalc: None) -> None:
"""Our chat chip has the fill of each chip of its key, and the white of slot 15."""
from meshterm.ui.pathline import _slot_colours
ours = _slot_colours(PathHop(f"{SELF_GLYPH} Me", key="1c", white_ink=True))
normal = _slot_colours(PathHop("Me", key="1c"))
assert ours.fill == normal.fill and ours.ink == slot_hex(15)
def test_the_picocalc_draws_chips_from_its_eight_backgrounds(picocalc: None) -> None:
"""The chip of a node is the dim twin of its hue, and the label is the hue itself.
The VT draws a background only from slots 0 to 7. Thus each fill is a dim slot, and each
label is the bright slot that the name has in arrow mode. A keyless chip is white on
light grey. Our star and a faded hop stand on the page, because the console has no dark
grey background. The row is a selected row. Its ``bold`` base style must not move a dim
fill to its bright twin, so each chip cell arrives without bold.
"""
hops = [
PathHop(SELF_GLYPH, you=True),
PathHop("Hilltop", key="1c"),
PathHop("Alice", key="0a", annotation="0a"),
PathHop("3d"),
PathHop("Ridge", key="1c", dim=True),
PathHop(SELF_GLYPH, you=True),
]
line = PathLine(hops).text() # auto: the console font has the chevrons
assert POWERLINE_SEP in line.plain
cells = _console_cells(_selected(line))
text = "".join(char for char, _ in cells)
dim_slots = {_rgb(slot_hex(n)) for n in range(8)}
for char, style in cells[2 : 2 + len(line.plain)]:
assert style.bg is None or style.bg in dim_slots, f"{char!r} on {style.bg}"
assert not style.flags & vt.BOLD, f"{char!r} is bold, so a dim slot turns bright"
def at(word: str) -> Style:
return cells[text.index(word)][1]
yellow, red = _style_hex(node_style("1c")), _style_hex(node_style("0a"))
assert (at("Hilltop").fg, at("Hilltop").bg) == (_rgb(yellow), _rgb(DIM_TWIN[yellow]))
assert (at("Alice").fg, at("Alice").bg) == (_rgb(red), _rgb(DIM_TWIN[red]))
assert at("(0a)").fg == _rgb(red) # the annotation also has the colour of the label
assert (at("3d").fg, at("3d").bg) == (_rgb(slot_hex(15)), _rgb(slot_hex(7)))
assert at("Ridge").bg in (None, _rgb(slot_hex(0))) # faded: on the page
assert at("Ridge").fg == _rgb(slot_hex(8))
assert at(SELF_GLYPH).bg in (None, _rgb(slot_hex(0)))
def test_a_picocalc_hash_label_is_one_colour(picocalc: None) -> None:
"""The rest of a hash has the hue of its lit bytes, and not a grey.
A grey after the lit byte was not readable on the green and the cyan fills (JP,
2026-10-06).
"""
for key in ("0a", "1c", "44", "70", "9a", "c4"):
line = PathLine([PathHop(f"{key}63c6", key=key, lit_bytes=1)]).text()
cells = _console_cells(line)
hue = _rgb(_style_hex(node_style(key)))
label = [style.fg for char, style in cells if char in f"{key}63c6" and char != " "]
assert label == [hue] * 6, key
def test_picocalc_seams_on_one_colour_and_the_composer_slot(picocalc: None) -> None:
"""Two chips of one colour join on a thin chevron in the hue. The slot is a break.
The thin chevron is the bright twin of the fill: the colour of the label on the chip.
The slot of the composer has no white background on the console. Thus it is a white
``+`` on the page, and the chips on each side close on a point and open on a notch.
"""
same = PathLine([PathHop("Mill", key="1a"), PathHop("Tower", key="20")]).text()
seam = same.plain.index(POWERLINE_THIN)
cell = _console_cells(same)[seam][1]
yellow = _style_hex(node_style("1a"))
assert (cell.fg, cell.bg) == (_rgb(yellow), _rgb(DIM_TWIN[yellow]))
slot = [
PathHop("Hilltop", key="1c"),
PathHop(CURSOR_GLYPH, cursor=True),
PathHop("Alice", key="0a"),
]
line = PathLine(slot).text()
cells = _console_cells(line)
plus = cells[line.plain.index(CURSOR_GLYPH)][1]
assert plus.fg == _rgb(slot_hex(15)) and plus.bg in (None, _rgb(slot_hex(0)))
assert line.plain.count(POWERLINE_SEP) == 2 # a point into the break, and a notch out of it
def test_every_picocalc_chip_glyph_is_in_its_font(picocalc: None) -> None:
"""Each mark of the chip language is in the console font, so nothing folds to ``?``."""
hops = [PathHop(SELF_GLYPH, you=True), PathHop("Hilltop", key="1c"), elision_hop()]
line = PathLine([*hops, PathHop("a"), PathHop("b")]).text()
drawn = [line, cut_to(line, 12), cut_mark(line, 1, ELIDE_HEAD)]
ink = "".join(text.plain for text in drawn)
assert {POWERLINE_SEP, POWERLINE_THIN, CRACK_TAIL, CRACK_HEAD, SELF_GLYPH} <= set(ink)
assert all(ord(char) in FONT_CODEPOINTS for char in ink)
assert fold_text(ink) == ink
def test_the_cursor_is_a_hop_of_its_own_in_either_mode() -> None:
"""The insertion point of the composer is a slot *in* the route, and not a gap between hops.
It renders as a hop, so chips stay chips and arrows stay arrows.
"""
hops = [PathHop("a"), PathHop(CURSOR_GLYPH, cursor=True), PathHop("b")]
plain = PathLine(hops, mode="plain").text()
assert plain.plain == f"a → {CURSOR_GLYPH} → b"
assert _styles(plain)[CURSOR_GLYPH] == "selected" # the block in reverse video
chips = PathLine(hops, mode="powerline").text()
assert chips.plain == f" a {POWERLINE_SEP} {CURSOR_GLYPH} {POWERLINE_SEP} b "
slot = next(s for s in chips.spans if chips.plain[s.start : s.end] == CURSOR_GLYPH)
assert str(slot.style).endswith(f"on {_style_hex('cursor')}") # the fill in the cursor white
def test_ellipsized_elides_the_middle_and_keeps_both_endpoints() -> None:
"""The fit removes the middle hops behind ``⋯`` and keeps both endpoints.
A route reads the origin and the destination first. A truncation at the right edge
removes the tail, and the tail is what must stay.
"""
hops = [PathHop(label) for label in ("AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF")]
line = PathLine(hops, mode="plain")
assert line.ellipsized(200).plain == line.text().plain # it fits, so it is unchanged
fitted = line.ellipsized(25)
assert fitted.cell_len <= 25
assert fitted.plain.startswith("AAAA")
assert fitted.plain.endswith("FFFF")
assert "⋯" in fitted.plain
def test_ellipsized_eats_the_head_when_told_to() -> None:
"""``ELIDE_HEAD`` does not spare the origin, and it keeps the tail instead.
The news of a breadcrumb is where the walk *is*. Thus the fit keeps as much of the tail
as the width holds, behind a ``⋯``.
"""
hops = [PathHop(label) for label in ("AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF")]
line = PathLine(hops, mode="plain")
fits = line.ellipsized(200, elide=ELIDE_HEAD).plain
assert fits == line.text().plain # it fits, so it is unchanged for each side
fitted = line.ellipsized(25, elide=ELIDE_HEAD)
assert fitted.cell_len <= 25
assert fitted.plain.startswith("⋯") # the origin also goes, unlike the fit on the tail side
assert fitted.plain.endswith("FFFF")
assert "AAAA" not in fitted.plain
def test_ellipsized_last_resort_truncates_a_single_giant_hop() -> None:
"""When even ``⋯ → last`` overflows, the classic truncation with an ellipsis is used."""
line = PathLine([PathHop("AAAAAAAAAA"), PathHop("BBBBBBBBBB")], mode="plain")
fitted = line.ellipsized(8)
assert fitted.cell_len <= 8
assert fitted.plain.endswith("…")
def _chips() -> PathLine:
"""Three keyed hops in chip mode. Each fill is a different hue from a hash."""
return PathLine(
[PathHop("AAAA", key="11aa"), PathHop("BBBB", key="22bb"), PathHop("CCCC", key="33cc")],
mode="powerline",
)
def test_cut_to_cracks_a_chip_in_its_own_fill_instead_of_ellipsizing() -> None:
"""A chip that the cut catches breaks off on a half block in its own fill.
The crack reads as a segment that continues. The ``…`` claims that a word was made
shorter.
"""
full = _chips().text()
fitted = cut_to(full, 14)
assert fitted.cell_len == 14
assert fitted.plain.endswith(CRACK_TAIL)
assert "…" not in fitted.plain
# The cut lands inside ``BBBB``, so the crack has the hue of BBBB, and not the hue of
# its neighbours.
assert str(fitted.spans[-1].style) == _fill("22bb")
assert cut_to(full, 200) is full # it fits, so there is no change, no copy, and no mark
def test_cut_to_leaves_arrow_lines_on_the_ellipsis() -> None:
"""Only chips crack, and an arrow line keeps the ellipsis.
It has no fill to shear. A shorter text is what happened, and the classic mark says so.
"""
fitted = cut_to(PathLine(_chips().hops, mode="plain").text(), 10)
assert fitted.cell_len <= 10
assert fitted.plain.endswith("…")
assert CRACK_TAIL not in fitted.plain
def test_cut_mark_mirrors_itself_and_reads_the_visible_side() -> None:
"""The head mark mirrors the tail mark, and each takes the fill of the nearest drawn cell.
This means a scan back for a tail cut, and a scan forward for a head cut.
"""
full = _chips().text()
body = full.plain.index("AAAA") # inside the first chip, in both scan directions
head, tail = cut_mark(full, body, ELIDE_HEAD), cut_mark(full, body, ELIDE_TAIL)
assert (head.plain, tail.plain) == (CRACK_HEAD, CRACK_TAIL)
assert str(head.style) == str(tail.style) == _fill("11aa")
# A cut that lands on the interlocked seam itself cracks in the field that the cell
# has: the chip *ahead*, whose fill is the background of the seam. Thus the mark reads
# as the next segment that begins and is sheared, and not as a colour that is not
# in the line.
seam = full.plain.index(POWERLINE_SEP)
assert str(cut_mark(full, seam, ELIDE_TAIL).style) == _fill("22bb")
def test_cut_mark_falls_back_to_the_ellipsis_off_a_chip() -> None:
"""Off a chip, the same question gives the muted ``…``.
The fallback is the whole test of the mode. Thus no caller must know which mode drew the
line.
"""
arrows = PathLine(_chips().hops, mode="plain").text()
for side in (ELIDE_HEAD, ELIDE_TAIL):
mark = cut_mark(arrows, 6, side)
assert mark.plain == "…"
assert str(mark.style) == "muted"
def test_cut_mark_cracks_only_inside_the_path_of_a_row() -> None:
"""A cut in the text beside a chip ends in the ellipsis. A cut in a chip still cracks.
A row can be a chip, then text: the recipient of a Courier entry, then its trigger. The
scan for a fill once went across the text into the chip. Then the row ended in a half
block in the fill of the chip, at the right edge and far from the chip. The scan now
stays inside the path that the row stamps (``PATH_INK``).
"""
row = Text()
row.append_text(_chips().text())
end = len(row.plain)
row.append(" when heard", style="muted")
after = cut_mark(row, end + 4, ELIDE_TAIL)
assert (after.plain, str(after.style)) == ("…", "muted")
inside = cut_mark(row, row.plain.index("BBBB"), ELIDE_TAIL)
assert inside.plain == CRACK_TAIL and str(inside.style) == _fill("22bb")
# The mirror: text before the chips, and a head cut in that text.
lead = Text("via ", style="muted")
lead.append_text(_chips().text())
before = cut_mark(lead, 1, ELIDE_HEAD)
assert (before.plain, str(before.style)) == ("…", "muted")
assert cut_mark(lead, lead.plain.index("AAAA"), ELIDE_HEAD).plain == CRACK_HEAD
def test_the_elision_sits_between_chips_rather_than_being_one() -> None:
"""Hops that MeshTerm removed from a path are a break *in* the ribbon, not a node ``⋯``.
The chip before the break closes onto the page. The mark is bare there, with no fill and
no padding. The chip after the break opens on its notch. The whole gap costs three
cells. A filled ``⋯`` chip with its pads and two seams costs seven cells, which is four
more cells of route on a line that by definition has little room.
"""
line = PathLine([PathHop(f"NODE{i:02d}", key=f"{i:02x}aa") for i in range(5)], mode="powerline")
fitted = line.ellipsized(30)
assert fitted.cell_len <= 30
at = fitted.plain.index("⋯")
assert fitted.plain[at - 1] == POWERLINE_SEP and fitted.plain[at + 1] == POWERLINE_SEP
styles = {fitted.plain[s.start : s.end]: str(s.style) for s in fitted.spans}
assert styles["⋯"] == "faint" # bare on the page: no ``on`` fill, and no pads
# Arrow mode needs no special case, because a bare label between two arrows *is* a gap.
plain = PathLine(line.hops, mode="plain").ellipsized(30)
assert " ⋯ " in plain.plain and "→ ⋯ →" in plain.plain
def test_elision_hop_is_the_one_definition_both_surfaces_share() -> None:
"""One elision definition serves both surfaces.
The widget and the trail of the mesh walk elide with the same mark *and* the same gap
semantics. Thus a head that the widget hid reads as a tail that the scroll hid.
"""
mark = elision_hop()
assert (mark.label, mark.gap, mark.dim) == ("⋯", True, True)
def test_wrapped_cracks_an_over_wide_lone_chip() -> None:
"""A chip that is wider than the content column is cracked and not folded.
This is the one place where a chip line is cut in the middle of a hop. The hop stands
alone, cracked, and still inside the budget.
"""
lines = PathLine([PathHop("N" * 40, key="11aa")], mode="powerline").wrapped(20)
assert len(lines) == 1
assert lines[0].cell_len <= 20
assert lines[0].plain.endswith(CRACK_TAIL)
def test_wrapped_breaks_at_hops_under_a_hanging_indent() -> None:
"""A wrapped plain path breaks at hops, under a hanging indent.
Lines that continue end with the ``→`` cue. Continuations hang at the indent, stepped in
by :data:`WRAP_OFFSET`. Each line keeps the full width.
"""
hops = [PathHop(label) for label in ("AAAA", "BBBB", "CCCC", "DDDD")]
lines = PathLine(hops, mode="plain").wrapped(16, indent=2)
assert [line.plain for line in lines] == ["AAAA → BBBB →", " CCCC → DDDD"]
assert lines[1].plain.startswith(" " * (2 + WRAP_OFFSET))
assert all(line.cell_len <= 16 for line in lines)
def test_wrapped_evens_the_lines_instead_of_widowing_the_tail() -> None:
"""A hop that misses the first line by a cell is not left alone below it.
Greedy packing puts ``us → a → b →`` on the first line and leaves ``us`` as a widow. The
fold takes the same number of lines in both cases, so it spreads the hops across them.
"""
hops = [PathHop(label) for label in ("us", "alpha", "bravo", "us")]
lines = PathLine(hops, mode="plain").wrapped(20, indent=0)
assert [line.plain for line in lines] == ["us → alpha →", " bravo → us"]
def test_wrapped_folds_a_faded_return_leg_at_its_turn() -> None:
"""A boomerang breaks where it turns: the composed leg is above, and its echo is below.
The dimmed hops are the mirrored return. A fold there gives the wrapped route a shape,
and not an arbitrary seam in the middle of a leg. MeshTerm takes it only when it costs
nothing. Here the fold at the turn costs no more lines than the greedy break.
"""
out = [PathHop(n) for n in ("me", "north-relay", "east-relay", "dest")]
back = [PathHop(n, dim=True) for n in ("east-relay", "north-relay", "me")]
lines = PathLine(out + back, mode="plain").wrapped(40, indent=0)
assert [line.plain for line in lines] == [
"me → north-relay → east-relay → dest →", # out, ending on the target
" east-relay → north-relay → me", # and the mirror by which it comes home
]
def test_wrapped_folds_a_walked_boomerang_at_its_turn_too() -> None:
"""A trace that answered dims nothing, but its route is still its own mirror.
Thus MeshTerm finds the turn from the sequence of hops itself. The walk folds in the
same place as the plan that armed it. One route has one shape.
"""
walked = [PathHop(n) for n in ("me", "north-relay", "east-relay", "dest")]
walked += [PathHop(n) for n in ("east-relay", "north-relay", "me")]
lines = PathLine(walked, mode="plain").wrapped(46, indent=0)
assert [line.plain for line in lines] == [
"me → north-relay → east-relay → dest →",
" east-relay → north-relay → me",
]
def test_wrapped_never_folds_before_a_lone_faded_landing() -> None:
"""A path whose only dim hop is the automatic return home keeps it on a real line.
Walks in path mode dim only that last ``us``. A fold at "the fade" there leaves it as a
widow. Thus the seam needs two hops on each side to count as a turn.
"""
hops = [PathHop(label) for label in ("us", "alpha", "bravo", "charlie")]
hops.append(PathHop("us", dim=True))
lines = PathLine(hops, mode="plain").wrapped(26, indent=0)
assert len(lines) == 2
assert lines[-1].plain != "us"
assert lines[-1].plain.endswith("→ us")
def test_wrapped_lines_always_fit_their_width() -> None:
"""No mix of hops, mode, width, or indent gives a line that is past the budget.
MeshTerm fits lines with arithmetic from the measurement of each hop, and does not
render each candidate group. Thus this test sweeps that model against what MeshTerm
really draws. The two can differ for lone hops that are too wide (truncated, with the
cue), and for chip lines (which have a closing edge that an arrow line does not have).
"""
rng = random.Random(7)
for _ in range(200):
hops = [
PathHop(
"N" * rng.randint(1, 16),
key=f"{i:02x}aa",
annotation=f"{i:02x}" if rng.random() < 0.5 else None,
dim=rng.random() < 0.3,
you=rng.random() < 0.2,
lit_bytes=rng.choice((0, 1, 2)),
)
for i in range(rng.randint(1, 9))
]
for mode in ("plain", "powerline"):
for width, indent in ((20, 0), (34, 2), (47, 7), (72, 16)):
lines = PathLine(hops, mode=mode).wrapped(width, indent=indent)
assert all(line.cell_len <= width for line in lines)
def test_wrapped_continuation_cue_follows_the_separator() -> None:
"""The continuation cue is the separator's own mark, and it is not always an arrow.
A line that commas join (the wire spec of the trace screen) continues on a comma. Thus
the spec stays exactly as it is.
"""
hops = [PathHop(label) for label in ("3d63ab99", "7f21cd01", "27aa1122")]
lines = PathLine(hops, mode="plain", separator=",").wrapped(20, indent=2)
assert lines[0].plain.endswith(",")
assert "→" not in "".join(line.plain for line in lines)
assert "".join(line.plain.strip() for line in lines) == "3d63ab99,7f21cd01,27aa1122"
def test_path_line_factory_matches_path_text_character_for_character() -> None:
"""``path_line`` renders the trace form in the same way as ``path_text`` did.
This is the migration bridge. It has device endpoints with annotated hashes, a named
hop, an unnamed hop with a lit prefix, and a dimmed tail. The characters and the styles
are the same.
"""
names = {"aa11bb": "Alice", "3d63ab": "Hub"}
hops = [None, "aa11bb", "77ccddee", "3d63ab", None]
kwargs = dict(
prefix_bytes=2,
self_name="Me",
show_hash=True,
hash_bytes=3,
device_hash="A1B2C3D4",
dim_from=3,
)
old = path_text(hops, lambda h: names.get(h, h), **kwargs)
new = path_line(hops, lambda h: names.get(h, h), mode="plain", **kwargs).text()
assert _char_styles(new) == _char_styles(old)
def test_path_line_factory_matches_path_text_hash_as_name_flavour() -> None:
"""The form for message paths also matches ``path_text``.
An unnamed hop is its own muted identity hash, with the byte by which it is addressed.
The guarantee of parity is the same.
"""
names = {"3d63abcdef00": "Hub"}
hops = ["3d63abcdef00", "e839f2aabb11"]
kwargs = dict(prefix_bytes=3, show_hash=True, hash_bytes=1, hash_as_name=True)
old = path_text(hops, lambda h: names.get(h, h), **kwargs)
new = path_line(hops, lambda h: names.get(h, h), mode="plain", **kwargs).text()
assert _char_styles(new) == _char_styles(old)
assert path_line([], lambda h: h).text().plain == path_text([], lambda h: h).plain
def test_path_line_factory_splices_the_cursor_slot_at_a_hop_index() -> None:
"""``cursor`` inserts the insertion slot at a hop index.
It names the position that a chosen hop takes. MeshTerm counts it over the rendered hops
and applies it after them, so the slot never changes what its neighbours show.
"""
hops = [None, "aa11bb", "3d63ab", None]
def built(cursor) -> str: # noqa: ANN001
line = path_line(hops, self_name="Me", bare_self=True, mode="plain", cursor=cursor)
return line.text().plain
assert built(None) == f"{SELF_GLYPH} → aa11bb → 3d63ab → {SELF_GLYPH}"
assert built(0) == f"{CURSOR_GLYPH} → {SELF_GLYPH} → aa11bb → 3d63ab → {SELF_GLYPH}"
assert built(2) == f"{SELF_GLYPH} → aa11bb → {CURSOR_GLYPH} → 3d63ab → {SELF_GLYPH}"
assert built(99).endswith(f"{SELF_GLYPH} → {CURSOR_GLYPH}") # limited to the tail
def test_wrapped_carries_the_cursor_like_any_other_hop() -> None:
"""A folded route cannot strand its insertion point.
The slot is a hop, so it lands on a line by itself, in any position and in any mode.
"""
labels = ("AAAA", "BBBB", "CCCC", "DDDD")
for at in range(len(labels) + 1):
hops = [PathHop(label) for label in labels]
hops.insert(at, PathHop(CURSOR_GLYPH, cursor=True))
lines = PathLine(hops, mode="plain").wrapped(16, indent=2)
assert sum(text.plain.count(CURSOR_GLYPH) for text in lines) == 1
assert all(text.cell_len <= 16 for text in lines)
carried = next(t for t in lines if CURSOR_GLYPH in t.plain)
assert any(str(s.style) == "selected" for s in carried.spans)
def test_wrapped_chip_lines_open_on_the_break_they_continue() -> None:
"""Chip wrapping never splits a chip, and each line opens on the break that it continues.
Each line that the path *outruns* ends on the pointed edge. The last line ends square,
because the route is finished. Each line *but the first* opens on the other half of the
break. This is the point that is notched out of its own fill in reverse video. Thus the
page shows through, and no trace of the line above bleeds down. The point always faces
the direction in which the path flows.
"""
hops = [
PathHop(label, key=key)
for label, key in (("AAAA", "aa"), ("BBBB", "77"), ("CCCC", "3d"), ("DDDD", "f2"))
]
lines = PathLine(hops, mode="powerline").wrapped(20, indent=2)
assert len(lines) == 2
assert lines[0].plain.startswith(" AAAA") # the square edge: this is the start
step = 2 + WRAP_OFFSET
assert lines[1].plain[:step].isspace() and lines[1].plain[step] == POWERLINE_SEP
# …past the line's own PATH_INK stamp, which covers the whole body from the same
# cell (it marks the run as a path line for the cursor fold, and it draws nothing).
carried = next(s for s in lines[1].spans if s.start == step and str(s.style) != PATH_INK)
assert str(carried.style) == f"{_fill('3d')} reverse"
assert lines[0].plain.endswith(POWERLINE_SEP) # …the path runs on past this line
assert lines[-1].plain.endswith(" ") # …and stops square on the pad of the last chip
assert all(line.cell_len <= 20 for line in lines)
def test_rounded_caps_finish_a_path_only_where_the_font_has_them(monkeypatch) -> None: # noqa: ANN001
"""Rounded caps finish a path only where the font really has them.
A full Nerd Font rounds the two *outer* ends of the path into a lozenge. A terminal that
has only the core glyphs makes *both* of them square. It appends nothing, and the
own pads of the outer chips are the edges. It never draws tofu. Interior breaks stay angled
in both cases. A rounded end reads as the path that stops. A pointed end at the finish
reads as the opposite: a route that is cut off in the middle of a walk.
"""
hops = [
PathHop(label, key=key)
for label, key in (("AAAA", "aa"), ("BBBB", "77"), ("CCCC", "3d"), ("DDDD", "f2"))
]
line = PathLine(hops, mode="powerline")
monkeypatch.setattr(pathline, "powerline_full", lambda: False)
assert line.text().plain.startswith(" AAAA")
assert line.text().plain.endswith("DDDD ")
# The point is still the end of a *wrapped* line, because there the route really goes on.
squared = line.wrapped(20, indent=2)
assert squared[0].plain.endswith(POWERLINE_SEP) and squared[-1].plain.endswith("DDDD ")
monkeypatch.setattr(pathline, "powerline_full", lambda: True)
assert line.text().plain.startswith(POWERLINE_ROUND_OPEN + " AAAA")
assert line.text().plain.endswith(POWERLINE_ROUND_CLOSE)
wrapped = line.wrapped(20, indent=2)
assert len(wrapped) == 2
assert wrapped[0].plain.startswith(POWERLINE_ROUND_OPEN) # the path opens here…
assert wrapped[0].plain.endswith(POWERLINE_SEP) # …but does not end here
assert wrapped[1].plain[2 + WRAP_OFFSET] == POWERLINE_SEP # it continues mid-path…
assert wrapped[1].plain.endswith(POWERLINE_ROUND_CLOSE) # …and it closes
assert all(text.cell_len <= 20 for text in wrapped)
def test_a_line_drawing_only_a_route_middle_wears_the_chevron_at_that_end(monkeypatch) -> None: # noqa: ANN001
"""A chain of relays has no endpoints of its own, so neither end can claim to be one.
The ``via`` field of a packet names the nodes that forwarded it. It does not name the
node that it came from, and it does not name the node that received it. A flat end there
says that the first relay *is* where the route began. Thus the line opens on the notch
and closes on the point instead. This is the same mark "there is more of this out
there" that a wrapped line uses. The flags give up the cap. A full Nerd Font rounds only
the ends that are real.
"""
hops = [PathHop(label, key=key) for label, key in (("AAAA", "aa"), ("BBBB", "77"))]
middle = PathLine(hops, mode="powerline", from_origin=False, to_destination=False)
monkeypatch.setattr(pathline, "powerline_full", lambda: True)
text = middle.text()
assert text.plain.startswith(POWERLINE_SEP + " AAAA") # the notch, and not the lozenge
assert text.plain.endswith("BBBB " + POWERLINE_SEP) # the point: the route goes on
opening = next(s for s in text.spans if s.start == 0 and str(s.style) != PATH_INK)
assert str(opening.style) == f"{_fill('aa')} reverse" # cut out of AAAA
# One end at a time: the half that *is* an endpoint keeps its cap in both cases.
head = PathLine(hops, mode="powerline", to_destination=False).text().plain
assert head.startswith(POWERLINE_ROUND_OPEN) and head.endswith(POWERLINE_SEP)
tail = PathLine(hops, mode="powerline", from_origin=False).text().plain
assert tail.startswith(POWERLINE_SEP) and tail.endswith(POWERLINE_ROUND_CLOSE)
monkeypatch.setattr(pathline, "powerline_full", lambda: False)
squared = PathLine(hops, mode="powerline").text().plain # the whole route, for the contrast
assert squared.startswith(" AAAA") and squared.endswith("BBBB ")
assert middle.text().plain.startswith(POWERLINE_SEP) # …the chevron is not the cap
assert middle.text().plain.endswith(POWERLINE_SEP)
def test_arrow_mode_says_a_route_middle_with_the_separator_it_joins_with() -> None:
"""A console has no chevrons to shear, so the arrow itself makes the same claim.
There is a leading ``→`` where the origin is not drawn, and a trailing ``→`` where the
destination is not drawn. This is the mark with which arrow mode already spells "the
path goes on" when a line wraps. Both modes tell the user the same thing.
"""
hops = [PathHop(label, key=key) for label, key in (("AAAA", "aa"), ("BBBB", "77"))]
assert PathLine(hops, mode="plain").text().plain == "AAAA → BBBB"
middle = PathLine(hops, mode="plain", from_origin=False, to_destination=False)
assert middle.text().plain == "→ AAAA → BBBB →"
assert PathLine(hops, mode="plain", from_origin=False).text().plain == "→ AAAA → BBBB"
assert PathLine(hops, mode="plain", to_destination=False).text().plain == "AAAA → BBBB →"
def test_a_wrapped_route_middle_pays_for_both_its_chevrons() -> None:
"""MeshTerm measures the marks, and does not append them past the budget, in both modes.
A line with only relays uses a cell at each outer end. A whole route uses this cell
only where the font has caps. Thus the fit must know about the cells. Also, the wrap
flags and the endpoint flags must not both draw at the same break.
"""
hops = [
PathHop(label, key=key)
for label, key in (("AAAA", "aa"), ("BBBB", "77"), ("CCCC", "3d"), ("DDDD", "f2"))
]
lines = PathLine(hops, mode="powerline", from_origin=False, to_destination=False).wrapped(
20, indent=2
)
assert len(lines) == 2
assert all(line.cell_len <= 20 for line in lines)
assert lines[0].plain.startswith(POWERLINE_SEP) # opens in the middle of the route…
assert lines[0].plain.endswith(POWERLINE_SEP) # …and the fold goes on
assert lines[-1].plain.endswith(POWERLINE_SEP) # …as does the route past the last chip
# Never both marks at one break. A fold that is also the head or the tail of the drawn
# route still draws one chevron there, and not two.
assert POWERLINE_SEP * 2 not in "".join(line.plain for line in lines)
folded = PathLine(hops, mode="plain", from_origin=False, to_destination=False).wrapped(
22, indent=2
)
assert len(folded) > 1 and all(line.cell_len <= 22 for line in folded)
assert folded[0].plain.startswith("→ AAAA") # the head mark, one time
assert folded[-1].plain.rstrip().endswith("→")
assert not folded[-1].plain.strip().startswith("→") # a continuation opens on its hop
def _seams(*hops: PathHop) -> list[tuple[str, str]]:
"""Each seam cell of a chip line as ``(glyph, style)``, in order."""
text = PathLine(list(hops), mode="powerline").text()
return [
(text.plain[s.start], str(s.style))
for s in text.spans
if text.plain[s.start] in (POWERLINE_SEP, POWERLINE_THIN)
]
def test_a_seam_between_two_of_one_colour_is_the_thin_chevron_in_its_label() -> None:
"""Where two chips have the same fill, the seam is the thin chevron in the label colour.
The interlock cannot draw a solid point in its own background. Thus MeshTerm draws the
join *on* the shared fill instead (powerline's own mark for a join inside one
colour), and the ribbon continues without a break. Its colour is the label of the
previous chip. It is the light half of the chip's own pair, so it is visible on the
fill, and nothing on the line has a colour that is not a colour of a chip. The faded
slate draws it in its muted text.
This is not a rare accident. A mirrored return leg is a run of hops that are identically
faded, by construction, and a stretch of keyless hops has one grey. In both cases the
seam stays a single cell. The fallback changes the interlock for the outline, and does
not change the width.
"""
hue = _style_hex(node_style("aa"))
glyph, style = _seams(PathHop("A", key="aa"), PathHop("B", key="aa"))[0]
assert (glyph, style) == (POWERLINE_THIN, f"{hue} on {_fill('aa')}")
assert _seams(PathHop("A", key="aa"), PathHop("B", key="77"))[0] == (
POWERLINE_SEP,
f"{_fill('aa')} on {_fill('77')}",
) # a blend, one cell
glyph, style = _seams(PathHop("A", dim=True), PathHop("B", dim=True))[0]
assert (glyph, style) == (POWERLINE_THIN, f"{_DIM_FG} on {_DIM_BG}")
def test_a_seam_blurs_by_perceived_distance_not_by_hue_gap() -> None:
"""Two fills that are less than ``SEAM_BLUR`` apart to the eye blur, as an exact match does.
The eye decides, and not the wheel. Sixteen steps of the first byte across the greens
are one colour to a user. Sixteen steps across the reds are two colours. Thus the same
gap of hue blurs on one side of the wheel and interlocks on the other. The test measures
the fills, because the solid point is a fill on a fill. The greys follow the same metric
with no exception. The keyless grey and the faded slate are far apart, and they keep
their seam.
"""
green = _seams(PathHop("A", key="4c"), PathHop("B", key="5c"))[0]
red = _seams(PathHop("A", key="00"), PathHop("B", key="10"))[0]
assert green[0] == POWERLINE_THIN and red[0] == POWERLINE_SEP
assert oklab.distance(_fill("4c"), _fill("5c")) < SEAM_BLUR
assert oklab.distance(_fill("00"), _fill("10")) > SEAM_BLUR
assert green[1] == f"{_style_hex(node_style('4c'))} on {_fill('5c')}"
wrapped = _seams(PathHop("A", key="fc"), PathHop("B", key="02"))[0]
assert wrapped[0] == POWERLINE_THIN # neighbours across the seam of the wheel, also
glyph, style = _seams(PathHop("A"), PathHop("B", dim=True))[0]
assert glyph == POWERLINE_SEP and " on " in style # two greys, which still interlock
def test_bare_self_stands_us_on_a_star_and_fades_both_ends() -> None:
"""``bare_self`` puts a ★ for us at both ends, and fades both.
To set out from us and to land back on us are fixtures of the route and not choices.
Thus they have the same automatic grey as a mirrored return leg. In chips, they have the
same dark slate, and not the loud white of you.
"""
hops = [None, *(f"{i:02x}aa" for i in range(6)), None]
line = path_line(
hops,
prefix_bytes=2,
self_name="Me",
show_hash=True,
device_hash="a1b2",
bare_self=True,
mode="plain",
)
text = line.text()
body = " → ".join(f"{i:02x}aa" for i in range(6))
assert text.plain == f"{SELF_GLYPH} → {body} → {SELF_GLYPH}"
assert "Me" not in text.plain and "a1b2" not in text.plain
stars = [s for s in text.spans if text.plain[s.start : s.end] == SELF_GLYPH]
assert [str(s.style) for s in stars] == ["faint", "faint"]
chips = PathLine(line.hops, mode="powerline").text()
star_fills = [str(s.style) for s in chips.spans if chips.plain[s.start : s.end] == SELF_GLYPH]
assert all("#ffffff" not in fill for fill in star_fills) # never the white of you
wrapped = path_line(
hops, prefix_bytes=2, self_name="Me", bare_self=True, mode="powerline"
).wrapped(28, indent=2)
assert len(wrapped) > 1
assert all(text.plain[2 + WRAP_OFFSET] == POWERLINE_SEP for text in wrapped[1:])
assert all(text.cell_len <= 28 for text in wrapped)
lines = path_line(
hops, prefix_bytes=2, self_name="Me", bare_self=True, mode="powerline"
).wrapped(28, indent=2)
assert len(lines) > 1
assert all(line.plain[2 + WRAP_OFFSET] == POWERLINE_SEP for line in lines[1:])
assert all(line.cell_len <= 28 for line in lines)
def test_bare_self_keeps_us_in_the_you_white_where_nothing_is_composed() -> None:
"""``dim_self=False`` makes the stars not muted: the fade means "not for you to compose".
A surface that only *shows* a walk (the boards and the record card of the Trophy case)
composes nothing. Thus there is no "not yours" to say, and our node reads in the ``you``
white of the app. This is the same colour that our star has in other places. A
``dim_from`` that reaches a star still fades it, so a mirrored return leg keeps its grey
in both cases.
"""
hops = [None, *(f"{i:02x}aa" for i in range(4)), None]
text = path_line(
hops, prefix_bytes=2, self_name="Me", bare_self=True, dim_self=False, mode="plain"
).text()
stars = [s for s in text.spans if text.plain[s.start : s.end] == SELF_GLYPH]
assert [str(s.style) for s in stars] == ["you", "you"]
# In chips the same identity is the white star of the map on the neutral dark grey. The
# fill is not a hue, because our end is a fixture and not a node that the user must
# tell from other nodes.
chips = path_line(
hops, prefix_bytes=2, self_name="Me", bare_self=True, dim_self=False, mode="powerline"
).text()
fills = [str(s.style) for s in chips.spans if chips.plain[s.start : s.end] == SELF_GLYPH]
assert fills == [f"bold #ffffff on {_YOU_BG}"] * 2
# The explicit fade still wins: a star inside a dimmed return leg stays grey.
faded = path_line(
hops,
prefix_bytes=2,
self_name="Me",
bare_self=True,
dim_self=False,
dim_from=3,
mode="plain",
).text()
tail = [s for s in faded.spans if faded.plain[s.start : s.end] == SELF_GLYPH]
assert [str(s.style) for s in tail] == ["you", "faint"]
def test_action_mark_rides_outside_the_width_budget() -> None:
"""The ``…`` for "opens further" costs the path no cells. It takes what is left, or nothing.
When the function kept room for it first, a route that filled its lane exactly cracked
on its last chip (JP, 2026-08-10). The row claimed that the walk ran on, when the walk
was finished, and the two cells that the hint needed were two cells that the user lost.
Thus MeshTerm fits the path first, and the mark lands in the space that is left.
"""
hops = [None, "3d63", "f2a1", None]
line = path_line(
hops, prefix_bytes=2, self_name="Me", bare_self=True, dim_self=False, mode="plain"
).text()
exact = line.cell_len
# A lane that the route fills exactly: the route stays whole, and the mark goes.
fitted = cut_to(line, exact, action=True)
assert fitted.plain == line.plain
assert fitted.cell_len == exact
# One cell of slack is still not enough for " …". The mark is two cells, all or nothing.
assert cut_to(line, exact + 1, action=True).plain == line.plain
# Room for both: the mark appears, and the whole line still fits the lane.
roomy = cut_to(line, exact + 2, action=True)
assert roomy.plain == line.plain + " …"
assert roomy.cell_len <= exact + 2
# A route that really overflows is cut as always, and then it has no room that is left.
cut = cut_to(line, exact - 4, action=True)
assert cut.cell_len == exact - 4 and not cut.plain.endswith(" …")
# The mark never makes a line wider than its lane, for any input.
assert with_action_mark(line, exact - 1).plain == line.plain
def test_action_mark_leaves_a_chip_path_closed_rather_than_cracked() -> None:
"""In chips, this is the whole purpose: a route that fits keeps its closing cap, not a crack."""
hops = [None, "3d63", "f2a1", None]
line = path_line(
hops, prefix_bytes=2, self_name="Me", bare_self=True, dim_self=False, mode="powerline"
).text()
fitted = cut_to(line, line.cell_len, action=True)
assert CRACK_TAIL not in fitted.plain
assert fitted.plain == line.plain
def test_hops_atom_counts_relays_and_says_direct_for_none() -> None:
"""The atom of the statistics line: ``direct`` for a route with no hops, else ``n hop(s)``.
A path with no relays is not "0 hops". ``direct`` is the app's own word for a
packet that went straight there, and :class:`PathLine` says it for an empty path also.
"""
assert hops_atom(0).plain == "direct"
assert hops_atom(1).plain == "1 hop"
assert hops_atom(4).plain == "4 hops"
assert hops_atom(-1).plain == "direct" # a caller that subtracted its endpoints two times
assert all(str(atom.style) == "muted" for atom in (hops_atom(0), hops_atom(3)))