Files
unios-tts/tests/test_ssml.py
T
Shaman 82c306ff69 init
2026-10-01 07:38:01 +03:00

132 lines
4.0 KiB
Python
Raw 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.
from app.segmentation import Segment
from app.ssml import (
PAUSE_DELIM,
build_ssml,
build_ssml_chunks,
explicit_pause_char,
strip_sentinels,
)
def seg(text, lang="ru", para=0):
return Segment(text=text, lang=lang, para=para)
# --- 2.1 структура <speak>/<p>/<s> и <prosody> ---
def test_single_segment_structure():
out = build_ssml([seg("Привет мир")])
assert out == "<speak><p><s>Привет мир</s></p></speak>"
def test_empty_returns_empty():
assert build_ssml([]) == ""
def test_paragraph_grouping_nesting():
out = build_ssml([seg("а", para=0), seg("б", para=0), seg("в", para=1)])
assert out.count("<p>") == 2
assert out.count("<s>") == 3
assert "<p><s>а</s><s>б</s></p>" in out
def test_no_prosody_by_default():
out = build_ssml([seg("привет")])
assert "<prosody" not in out
def test_prosody_rate_present_once():
out = build_ssml([seg("а"), seg("б")], rate="fast")
assert out.count("<prosody") == 1
assert 'rate="fast"' in out
def test_prosody_rate_and_pitch():
out = build_ssml([seg("а")], rate="slow", pitch="high")
assert '<prosody rate="slow" pitch="high">' in out
assert out.endswith("</prosody></speak>")
def test_prosody_medium_omitted():
out = build_ssml([seg("а")], rate="medium", pitch="medium")
assert "<prosody" not in out
# --- 2.2 sentinel -> <break>, вырезание PUA ---
def test_sentinel_becomes_single_break():
out = build_ssml([seg(f"а{PAUSE_DELIM}б")])
assert out.count("<break") == 1
assert 'time="150ms"' in out
assert PAUSE_DELIM not in out
def test_explicit_pause_to_break_ms():
out = build_ssml([seg("а" + explicit_pause_char(800) + "б")])
assert 'time="800ms"' in out
assert "а<break" in out and "б" in out
def test_explicit_pause_clamped_in_builder():
out = build_ssml([seg("а" + explicit_pause_char(9999) + "б")])
assert 'time="5000ms"' in out
def test_break_not_escaped():
out = build_ssml([seg(f"а{PAUSE_DELIM}б")])
assert "&lt;break" not in out
# --- экранирование текста ---
def test_text_is_xml_escaped():
out = build_ssml([seg("a & b <c> d")])
assert "&amp;" in out and "&lt;" in out and "&gt;" in out
assert "<c>" not in out
# --- 2.4 разбиение по абзацам/лимиту ---
def test_chunking_splits_into_valid_documents():
segs = [seg("предложение номер " + str(i) + " достаточно длинное") for i in range(20)]
chunks = build_ssml_chunks(segs, max_chars=200)
assert len(chunks) >= 2
for c in chunks:
assert c.startswith("<speak>") and c.endswith("</speak>")
# ни одно предложение не потеряно и не разорвано
assert sum(c.count("<s>") for c in chunks) == len(segs)
def test_chunking_respects_max_chars():
segs = [seg("с" * 60) for _ in range(10)]
chunks = build_ssml_chunks(segs, max_chars=200)
assert len(chunks) > 1
for c in chunks:
assert c.count("<s>") >= 1
def test_paragraphs_never_split_mid_segment():
segs = [seg("маленький абзац", para=0), seg("другой абзац", para=1)]
chunks = build_ssml_chunks(segs, max_chars=5000)
assert len(chunks) == 1
assert "<p><s>маленький абзац</s></p>" in chunks[0]
assert "<p><s>другой абзац</s></p>" in chunks[0]
def test_chunking_applies_prosody_to_each_document():
segs = [seg("текст " * 30) for _ in range(10)]
chunks = build_ssml_chunks(segs, rate="fast", max_chars=200)
assert len(chunks) >= 2
for c in chunks:
assert 'rate="fast"' in c
# --- strip_sentinels (для webui-отображения) ---
def test_strip_sentinels_removes_all_and_collapses_spaces():
raw = f"он {PAUSE_DELIM} сказал {explicit_pause_char(500)} слово"
clean = strip_sentinels(raw)
assert PAUSE_DELIM not in clean
assert explicit_pause_char(500) not in clean
assert " " not in clean