Files
UmbraAudio/tests/test_languages.py
B.D. Murphy bf83bb35cc Merge minieq/main: Apache-2.0 LICENSE, NOTICE and SPDX headers
The remote had one commit this branch did not (03f8a85), which adds the
Apache-2.0 LICENSE, the NOTICE and SPDX headers to every .py and .sh
file. The merge was clean - the headers sit at the top of each file and
my changes are further down.

The new files this branch added (languages.py, translations.py, the
cluster package, the new tests and scripts) were written after the
merge base, so the licence commit never saw them. They now carry the
same header, so the convention holds across the whole tree.

Nothing else changed. 883 tests passed / 8 failed - the same 8
pre-existing failures.
2026-11-06 17:12:24 -08:00

105 lines
3.7 KiB
Python

# Copyright 2026 Umbra Tools (umbratools.com)
# SPDX-License-Identifier: Apache-2.0
"""Tests for the language registry (spec 31 section 5).
The registry is the single source of truth for what this system can speak, so
these tests pin the two things that would silently mis-voice audio: the
voiceable/gap split, and the exact strings the TTS model expects.
"""
from __future__ import annotations
import re
from pathlib import Path
from umbraaudio import languages as lang
REPO = Path(__file__).resolve().parents[1]
def test_voiceable_count_is_ten() -> None:
assert len(lang.QWEN_LANGUAGES) == 10
assert sum(1 for x in lang.QWEN_LANGUAGES if x.tts_name) == 10
def test_gap_languages_are_not_voiceable() -> None:
assert len(lang.GAP_LANGUAGES) == 5
for gap in lang.GAP_LANGUAGES:
assert gap.tts_name is None
assert not lang.is_voiceable(gap.code)
assert lang.is_known(gap.code), "gap languages are known, just not speakable"
def test_gap_set_is_exactly_the_five_unsupported() -> None:
# spec 30 section 8 named these five; a sixth appearing here silently would
# change what the product claims to support.
assert {g.code for g in lang.GAP_LANGUAGES} == {"nl", "no", "pl", "ar", "hi"}
def test_no_duplicate_codes_or_names() -> None:
codes = [x.code for x in lang.ALL_LANGUAGES]
names = [x.name for x in lang.ALL_LANGUAGES]
assert len(codes) == len(set(codes))
assert len(names) == len(set(names))
def test_codes_are_lowercase_iso() -> None:
for x in lang.ALL_LANGUAGES:
assert re.fullmatch(r"[a-z]{2}", x.code), x
def test_normalize_accepts_code_name_and_model_string() -> None:
for x in lang.ALL_LANGUAGES:
assert lang.normalize(x.code) == x.code
assert lang.normalize(x.code.upper()) == x.code
assert lang.normalize(x.name) == x.code
assert lang.normalize(f" {x.name.lower()} ") == x.code
def test_normalize_rejects_unknown_without_raising() -> None:
# spec 31 section 8: an unrecognised language is a finding, not a crash.
for bad in (None, "", " ", "klingon", "zz"):
assert lang.normalize(bad) is None
def test_tts_name_is_the_model_string() -> None:
assert lang.tts_name("es") == "spanish"
assert lang.tts_name("Spanish") == "spanish"
assert lang.tts_name("zh") == "chinese"
assert lang.tts_name("nl") is None
def test_language_name_falls_back_gracefully() -> None:
assert lang.language_name("fr") == "French"
assert lang.language_name("xx") == "xx"
assert lang.language_name(None) == "unknown"
def test_refusal_reason_is_explicit() -> None:
assert lang.refusal_reason("en") is None
assert lang.refusal_reason(None) is None
nl = lang.refusal_reason("nl")
assert nl is not None and "'nl'" in nl and "Dutch" in nl
xx = lang.refusal_reason("xx")
assert xx is not None and "unrecognised" in xx
def test_origins_cover_the_three_sources() -> None:
assert set(lang.ORIGINS) == {"unknown", "umbratrans", "operator"}
def test_matches_the_working_reference_map() -> None:
"""The renderer that actually produces 10 languages is the authority.
If the registry and render_lang_test.py disagree, one of them is wrong and
the audio is mis-voiced. This is the test that catches it.
"""
src = (REPO / "scripts" / "render_lang_test.py").read_text(encoding="utf-8")
block = re.search(r"LANG_CODES = \{(.*?)\}", src, re.S)
assert block is not None, "LANG_CODES not found - did the renderer change?"
pairs = dict(re.findall(r'"([A-Z]{2})": "(\w+)"', block.group(1)))
assert len(pairs) == 10
for code, model_name in pairs.items():
assert lang.tts_name(code.lower()) == model_name, (code, model_name)