132 lines
4.0 KiB
Python
132 lines
4.0 KiB
Python
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 "<break" not in out
|
||
|
||
|
||
# --- экранирование текста ---
|
||
|
||
def test_text_is_xml_escaped():
|
||
out = build_ssml([seg("a & b <c> d")])
|
||
assert "&" in out and "<" in out and ">" 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
|