Files
MeshTerm/tests/test_termfont.py
T
Jean-Pierre MartineauandClaude Opus 5 92d31a3191 Every file is spelled the way the formatter spells it
`ruff format .` across the repo: 153 files, no behaviour change. This is the
second gate CONTRIBUTING.md promises, and until now it reported 154 files needing
reformat — so a first contributor ran the command the guide told them to run and
could not tell their own diff from the background.

Landed on its own, with nothing else in it, because a whole-repo reformat is only
safe to skip past in blame if it is provably nothing but layout. It was checked
that way rather than assumed: every changed file was formatted in memory and
compared by full `ast.dump()`, and 152 of 153 come out structurally identical
(the one exception was a docstring opening on a quote character, fixed in the
commit before this one). The string and comment token streams were diffed
separately, so a quote flip or an edited comment could not hide inside a
re-layout — five quote-style normalisations and sixteen implicit-concatenation
joins, all value-identical, and one commented-out line gaining the space after
its `#`. `meshterm specimen` is byte-identical on both platforms, and the suite
is 1888 green, gallery gate and `test_theme16` included.

What actually changed, in order of how much of the diff it is: wrapped calls and
signatures rejoin onto one line where they fit inside the hundred columns; the
magic trailing comma expands what it is there to expand; complex slices gain
their spaces (`[len(noise) :]`); a few redundant parens go. The tables that are
laid out to be read were fenced in the previous commit and are untouched here.

The aligned trailing-comment column is gone from about ninety lanes that were not
fenced — including the `heat.*` ladder in `MESH_THEME`, which cannot be fenced
where it sits. Every comment stays on the entry it describes, so nothing there
reads wrong; it just no longer lines up.

Add this commit to `.git-blame-ignore-revs`, which follows immediately.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 01:07:19 -04:00

255 lines
10 KiB
Python

"""Terminal-font detection tests: the recommended list, the ladder, the verdict.
Everything runs against injected environments and temp settings files — no test
touches the real registry, the real Win32 console, or the machine's actual fonts —
so the suite decides the same verdicts on any box.
"""
from __future__ import annotations
import json
from pathlib import Path
from meshterm.ui.termfont import (
CORE,
FULL,
NERD_FONT_GENERIC,
NONE,
UNKNOWN,
RecommendedFont,
_powerline_support,
_read_jsonc,
_vscode_default_face,
_vscode_face,
_windows_terminal_face,
detect_terminal_font,
installed_recommended,
match_recommended,
normalize_face,
primary_family,
)
# --- name matching ----------------------------------------------------------------
def test_primary_family_takes_the_first_of_a_css_list() -> None:
"""VS Code stores comma lists with optional quotes; the head family is judged."""
assert primary_family("'Hack Nerd Font Mono', monospace") == "Hack Nerd Font Mono"
assert primary_family('"Fira Code", Consolas') == "Fira Code"
assert primary_family("Cascadia Mono") == "Cascadia Mono"
def test_normalize_face_folds_case_quotes_and_spacing() -> None:
"""Matching is done on a lower-cased, single-spaced, unquoted form."""
assert normalize_face(" 'Hack Nerd Font' ") == "hack nerd font"
assert normalize_face('"MesloLGS NF"') == "meslolgs nf"
def test_match_recommended_knows_the_nerd_font_spellings() -> None:
"""The long name and the v3 short suffix both land on the same full entry."""
assert match_recommended("Hack Nerd Font Mono").name == "Hack Nerd Font"
assert match_recommended("Hack NFM").name == "Hack Nerd Font"
assert match_recommended("MesloLGS NF").coverage == FULL # powerlevel10k's install
def test_match_recommended_orders_full_patches_before_core_families() -> None:
"""A full patched family is matched ahead of the core family it is built on.
``JetBrainsMono Nerd Font`` must hit its full entry, never the plain ``JetBrains
Mono`` core prefix — first match wins, so the full entries lead.
"""
assert match_recommended("JetBrainsMono Nerd Font Mono").coverage == FULL
assert match_recommended("JetBrains Mono").coverage == CORE
def test_match_recommended_core_natives_and_strangers() -> None:
"""Stock coder fonts with native triangles read core; anything else, nothing."""
assert match_recommended("Fira Code").coverage == CORE
assert match_recommended("Source Code Variable").coverage == CORE
assert match_recommended("Consolas") is None
assert match_recommended(None) is None
def test_match_recommended_generic_nerd_rule_catches_unlisted_patches() -> None:
"""Any family carrying the Nerd Font branding has the full block patched in."""
assert match_recommended("Comic Shanns Mono Nerd Font") is NERD_FONT_GENERIC
assert match_recommended("Terminess NF") is NERD_FONT_GENERIC
# --- settings-file reading --------------------------------------------------------
def test_read_jsonc_survives_comments_and_trailing_commas(tmp_path: Path) -> None:
"""Both WT and VS Code write JSON-with-comments; the reader shrugs it off."""
path = tmp_path / "settings.json"
path.write_text(
"{\n"
" // the font\n"
' "editor.fontFamily": "Hack NFM", /* inline */\n'
' "url": "https://example.org//not-a-comment",\n'
' "list": [1, 2,],\n'
"}\n",
encoding="utf-8",
)
data = _read_jsonc(path)
assert data == {
"editor.fontFamily": "Hack NFM",
"url": "https://example.org//not-a-comment",
"list": [1, 2],
}
broken = tmp_path / "broken.json"
broken.write_text("{nope", encoding="utf-8")
assert _read_jsonc(broken) is None
def _wt_env(tmp_path: Path, settings: dict, guid: str = "{abc-123}") -> dict:
"""A fake Windows Terminal environment around a written settings file."""
folder = tmp_path / "Microsoft" / "Windows Terminal"
folder.mkdir(parents=True, exist_ok=True)
(folder / "settings.json").write_text(json.dumps(settings), encoding="utf-8")
return {"LOCALAPPDATA": str(tmp_path), "WT_SESSION": "s", "WT_PROFILE_ID": guid}
def test_windows_terminal_face_resolves_profile_then_defaults(tmp_path: Path) -> None:
"""The session profile's ``font.face`` wins; ``profiles.defaults`` backs it up."""
env = _wt_env(
tmp_path,
{
"profiles": {
"defaults": {"font": {"face": "Cascadia Code PL"}},
"list": [{"guid": "{ABC-123}", "font": {"face": "Hack Nerd Font Mono"}}],
},
},
)
assert _windows_terminal_face(env) == "Hack Nerd Font Mono" # guid case-folded
env2 = _wt_env(
tmp_path,
{
"profiles": {
"defaults": {"font": {"face": "Cascadia Code PL"}},
"list": [{"guid": "{other}"}],
},
},
)
assert _windows_terminal_face(env2) == "Cascadia Code PL"
def test_windows_terminal_face_reads_legacy_and_defaults_to_cascadia(tmp_path: Path) -> None:
"""The old flat ``fontFace`` key still counts; nothing set means the built-in."""
env = _wt_env(
tmp_path,
{
"profiles": {"list": [{"guid": "{abc-123}", "fontFace": "MesloLGS NF"}]},
},
)
assert _windows_terminal_face(env) == "MesloLGS NF"
env2 = _wt_env(tmp_path, {"profiles": {"list": [{"guid": "{abc-123}"}]}})
assert _windows_terminal_face(env2) == "Cascadia Mono"
assert _windows_terminal_face({"LOCALAPPDATA": str(tmp_path / "nowhere")}) == "Cascadia Mono"
def test_vscode_face_precedence_terminal_over_editor_workspace_over_user(
tmp_path: Path,
) -> None:
"""VS Code's font settings are read in precedence order.
``terminal.integrated.fontFamily`` beats ``editor.fontFamily``, and within a key
the workspace file beats the user file. The value's head family is the one judged.
"""
workspace = tmp_path / "repo"
(workspace / ".vscode").mkdir(parents=True)
(workspace / ".vscode" / "settings.json").write_text(
'{"editor.fontFamily": "Consolas"}', encoding="utf-8"
)
appdata = tmp_path / "appdata"
user = appdata / "Code" / "User"
user.mkdir(parents=True)
(user / "settings.json").write_text(
'{"terminal.integrated.fontFamily": "\'Hack Nerd Font Mono\', monospace"}',
encoding="utf-8",
)
env = {"APPDATA": str(appdata)}
assert _vscode_face(env, workspace) == "Hack Nerd Font Mono" # terminal.* wins
(user / "settings.json").write_text("{}", encoding="utf-8")
assert _vscode_face(env, workspace) == "Consolas" # workspace editor fallback
(workspace / ".vscode" / "settings.json").write_text("{}", encoding="utf-8")
assert _vscode_face(env, workspace) == _vscode_default_face()
# --- the ladder -------------------------------------------------------------------
def test_detect_terminal_font_ladder(tmp_path: Path) -> None:
"""The terminal is identified by working down a ladder of markers.
Windows Terminal first, then VS Code, then a genuine conhost — and ``TERM`` being
set disqualifies the conhost probe, since some other emulator is hosting it.
"""
wt = detect_terminal_font(_wt_env(tmp_path, {"profiles": {"list": []}}))
assert wt is not None and wt.source == "windows-terminal"
code = detect_terminal_font({"TERM_PROGRAM": "vscode", "APPDATA": str(tmp_path)}, cwd=tmp_path)
assert code is not None and code.source == "vscode"
con = detect_terminal_font({}, conhost_probe=lambda: "Consolas")
assert con is not None and (con.face, con.source) == ("Consolas", "conhost")
assert detect_terminal_font({"TERM": "xterm"}, conhost_probe=lambda: "Consolas") is None
def test_powerline_support_env_override_always_wins() -> None:
"""``MESHTERM_POWERLINE`` is the user's word: off, on/full, or core."""
assert _powerline_support({"MESHTERM_POWERLINE": "0"}).level == NONE
assert _powerline_support({"MESHTERM_POWERLINE": "off"}).level == NONE
on = _powerline_support({"MESHTERM_POWERLINE": "1", "WT_SESSION": "s"})
assert (on.level, on.source) == (FULL, "env")
assert _powerline_support({"MESHTERM_POWERLINE": "core"}).level == CORE
def test_powerline_support_matched_font_sets_the_level(tmp_path: Path) -> None:
"""A recommended face read from the terminal's own config decides coverage."""
env = _wt_env(
tmp_path,
{
"profiles": {"list": [{"guid": "{abc-123}", "font": {"face": "Hack Nerd Font Mono"}}]},
},
)
verdict = _powerline_support(env)
assert verdict.level == FULL
assert verdict.source == "font:windows-terminal"
assert verdict.matched is not None and verdict.matched.name == "Hack Nerd Font"
def test_powerline_support_renderer_fallback_and_honest_none(tmp_path: Path) -> None:
"""An unmatched face still earns core where the renderer can supply the glyphs.
On Windows Terminal that is the bundled-symbol fallback; the same face on a bare
conhost is an honest ``none``.
"""
env = _wt_env(tmp_path, {"profiles": {"list": [{"guid": "{abc-123}"}]}})
wt = _powerline_support(env) # face resolves to Cascadia Mono → no match
assert (wt.level, wt.source) == (CORE, "renderer:windows-terminal")
con = _powerline_support({}, conhost_probe=lambda: "Consolas")
assert (con.level, con.source, con.face) == (NONE, "font:conhost", "Consolas")
def test_powerline_support_kitty_ssh_and_unknown() -> None:
"""A glyph-drawing terminal earns core by its marker; anything else stays unknown.
ssh sessions and terminals we do not recognise are unknown rather than none: the
font lives on glass we cannot see.
"""
kitty = _powerline_support({"TERM": "xterm-kitty"})
assert (kitty.level, kitty.source) == (CORE, "renderer:kitty")
ssh = _powerline_support({"TERM": "xterm", "SSH_CONNECTION": "1.2.3.4"})
assert (ssh.level, ssh.source) == (UNKNOWN, "ssh")
lost = _powerline_support({"TERM": "xterm-256color"})
assert (lost.level, lost.source) == (UNKNOWN, "unknown")
def test_installed_recommended_never_raises() -> None:
"""Scanning the machine for installed fonts never raises.
It is best-effort context for a future nudge screen, so whatever the platform
answers, the result is a RecommendedFont or None.
"""
result = installed_recommended()
assert result is None or isinstance(result, RecommendedFont)