Spaces:
Sleeping
Sleeping
Claude Opus 4.8 (1M context)
Fix turn-audio splice drift; harden Turn Review; add Quick Synthesize tab
c198355 Download tests/test_tts_offset_map.py from GarchenArchive/ArchiveAI-TTS: direct link, hf CLI and curl.
- Browser
- Download file 6.02 kB
-
https://huggingface.co/spaces/GarchenArchive/ArchiveAI-TTS/resolve/main/tests/test_tts_offset_map.py
- Command line
-
hf download hf://spaces/GarchenArchive/ArchiveAI-TTS/tests/test_tts_offset_map.py
-
curl -L -o test_tts_offset_map.py https://huggingface.co/spaces/GarchenArchive/ArchiveAI-TTS/resolve/main/tests/test_tts_offset_map.py
6.02 kB
| """Tests for tts/core/offset_map.py β pure Python, no mocking required | |
| except where noted. build_offset_map is generic over any (start, end) span | |
| list; most tests use word_spans() since that's how app.py calls it, but the | |
| function itself doesn't care what produced the spans.""" | |
| import pytest | |
| from tts.core.glossary import substitute_with_glossary | |
| from tts.core.offset_map import build_offset_map | |
| from tts.utils.text import word_spans | |
| class TestBuildOffsetMap: | |
| def test_single_word_no_glossary(self): | |
| raw = "Hello" | |
| result = build_offset_map(raw, word_spans(raw), {}) | |
| assert result == [ | |
| {"raw_start": 0, "raw_end": 5, "sub_start": 0, "sub_end": 5, "text": "Hello"} | |
| ] | |
| def test_multiple_words_no_glossary_matches(self): | |
| raw = "Hello world today" | |
| result = build_offset_map(raw, word_spans(raw), {}) | |
| assert [e["text"] for e in result] == ["Hello", "world", "today"] | |
| # with no substitution, raw and substituted offsets are identical | |
| for entry in result: | |
| assert entry["raw_start"] == entry["sub_start"] | |
| assert entry["raw_end"] == entry["sub_end"] | |
| def test_maps_offsets_through_a_length_changing_substitution(self): | |
| raw = "Garchen speaks softly" | |
| glossary = {"Garchen": "gar-chen"} | |
| result = build_offset_map(raw, word_spans(raw), glossary) | |
| assert result is not None | |
| substituted_whole = substitute_with_glossary(raw, glossary) | |
| first, second, third = result | |
| assert raw[first["raw_start"]:first["raw_end"]] == "Garchen" | |
| assert substituted_whole[first["sub_start"]:first["sub_end"]] == "gar-chen" | |
| assert substituted_whole[second["sub_start"]:second["sub_end"]] == "speaks" | |
| assert substituted_whole[third["sub_start"]:third["sub_end"]] == "softly" | |
| def test_a_multi_word_span_composes_correctly(self): | |
| # A caller can combine several adjacent word entries (e.g. the words | |
| # touched by a drag-selection) into one (sub_start, sub_end) range β | |
| # verifies that composition is just first.sub_start..last.sub_end. | |
| raw = "Garchen Rinpoche speaks clearly" | |
| glossary = {"Garchen": "gar-chen", "Rinpoche": "rin-po-cheh"} | |
| result = build_offset_map(raw, word_spans(raw), glossary) | |
| substituted_whole = substitute_with_glossary(raw, glossary) | |
| combined_sub_start = result[0]["sub_start"] | |
| combined_sub_end = result[1]["sub_end"] | |
| assert substituted_whole[combined_sub_start:combined_sub_end] == "gar-chen rin-po-cheh" | |
| def test_sub_spans_are_contiguous_and_cover_whole_substituted_text(self): | |
| raw = "Garchen speaks. Rinpoche listens." | |
| glossary = {"Garchen": "gar-chen", "Rinpoche": "rin-po-cheh"} | |
| result = build_offset_map(raw, word_spans(raw), glossary) | |
| substituted_whole = substitute_with_glossary(raw, glossary) | |
| assert result[-1]["sub_end"] == len(substituted_whole) | |
| for a, b in zip(result, result[1:]): | |
| assert a["sub_end"] <= b["sub_start"] | |
| def test_empty_spans_returns_none(self): | |
| assert build_offset_map("", [], {}) is None | |
| def test_tag_spans_left_untouched_in_mapping(self): | |
| raw = 'Hi <break time="1.5s" /> there.' | |
| glossary = {"time": "tyme"} | |
| spans = word_spans(raw) | |
| result = build_offset_map(raw, spans, glossary) | |
| assert result is not None | |
| substituted_whole = substitute_with_glossary(raw, glossary) | |
| assert 'time="1.5s"' in substituted_whole | |
| tag_entry = next(e for e in result if e["text"].startswith("<break")) | |
| assert substituted_whole[tag_entry["sub_start"]:tag_entry["sub_end"]] == tag_entry["text"] | |
| def test_glossary_drift_is_detectable_against_a_stored_alignment(self): | |
| # An alignment produced at generation time is keyed to the substituted | |
| # string from THAT glossary. On reload, handle_load_project rebuilds the | |
| # offset map with the CURRENT glossary and must detect if the glossary | |
| # changed, because the rebuilt map would otherwise index a different | |
| # substituted string than the alignment (β wrong-region splices). The | |
| # detector it uses is: substitute_with_glossary(text, current) must equal | |
| # the joined alignment characters. This test pins that invariant. | |
| raw = "Garchen speaks" | |
| gen_glossary = {"Garchen": "gar-chen"} | |
| # The alignment's character string == the generation-time substitution. | |
| alignment_chars = list(substitute_with_glossary(raw, gen_glossary)) | |
| # Same glossary on reload β detector says consistent, map is trusted. | |
| assert substitute_with_glossary(raw, gen_glossary) == "".join(alignment_chars) | |
| # A length-changing glossary edit β detector fires (map must be dropped), | |
| # even though build_offset_map itself still returns a self-consistent map. | |
| changed_glossary = {"Garchen": "garchen"} | |
| assert build_offset_map(raw, word_spans(raw), changed_glossary) is not None | |
| assert substitute_with_glossary(raw, changed_glossary) != "".join(alignment_chars) | |
| def test_returns_none_when_piecewise_reconstruction_would_diverge(self, monkeypatch): | |
| # Force a substitution function whose output for a piece depends on | |
| # something other than that piece's own content (e.g. its position | |
| # in the overall call sequence) β piecewise application can then | |
| # never match a single whole-text call, so the safety net must | |
| # refuse to hand back an offset map rather than risk misaligned | |
| # splicing later. | |
| import tts.core.offset_map as mod | |
| calls = {"n": 0} | |
| def _inconsistent(text, glossary): | |
| calls["n"] += 1 | |
| return text.upper() if calls["n"] == 1 else text | |
| monkeypatch.setattr(mod, "substitute_with_glossary", _inconsistent) | |
| raw = "First word second" | |
| assert build_offset_map(raw, word_spans(raw), {}) is None | |