feat: Add stubs for soundfile and static_ffmpeg modules to facilitate testing

This commit is contained in:
JB
2025-11-11 17:00:58 -08:00
parent 504b5ab5e5
commit 028384e6ee
9 changed files with 424 additions and 54 deletions
+75 -29
View File
@@ -2,17 +2,33 @@ from __future__ import annotations
import pytest
from abogen.kokoro_text_normalization import normalize_roman_numeral_titles
from abogen.web.conversion_runner import _normalize_for_pipeline
from abogen.kokoro_text_normalization import (
DEFAULT_APOSTROPHE_CONFIG,
normalize_for_pipeline,
normalize_roman_numeral_titles,
)
from abogen.normalization_settings import (
apply_overrides as apply_normalization_overrides,
build_apostrophe_config,
get_runtime_settings,
)
from abogen.spacy_contraction_resolver import resolve_ambiguous_contractions
SPACY_RESOLVER_AVAILABLE = bool(resolve_ambiguous_contractions("It's been a long time."))
def _normalize_text(text: str, *, normalization_overrides: dict[str, object] | None = None) -> str:
runtime_settings = get_runtime_settings()
if normalization_overrides:
runtime_settings = apply_normalization_overrides(runtime_settings, normalization_overrides)
config = build_apostrophe_config(settings=runtime_settings, base=DEFAULT_APOSTROPHE_CONFIG)
return normalize_for_pipeline(text, config=config, settings=runtime_settings)
def test_title_abbreviations_are_expanded():
text = "Dr. Watson met Mr. Holmes and Ms. Hudson."
normalized = _normalize_for_pipeline(text)
normalized = _normalize_text(text)
assert "Doctor" in normalized
assert "Mister" in normalized
assert "Miz" in normalized
@@ -20,25 +36,25 @@ def test_title_abbreviations_are_expanded():
def test_suffix_abbreviations_are_expanded_with_case_preserved():
text = "John Doe Jr. spoke to JANE DOE SR. about the estate."
normalized = _normalize_for_pipeline(text)
normalized = _normalize_text(text)
assert "John Doe Junior" in normalized
assert "JANE DOE SENIOR" in normalized
def test_missing_terminal_punctuation_is_added():
normalized = _normalize_for_pipeline("Chapter 1")
normalized = _normalize_text("Chapter 1")
assert normalized.endswith(".")
def test_terminal_punctuation_respects_closing_quotes():
normalized = _normalize_for_pipeline('"Chapter 1"')
normalized = _normalize_text('"Chapter 1"')
compact = normalized.replace(" ", "")
assert compact.endswith('."')
def test_normalization_preserves_spacing_around_quotes_and_hyphen():
sample = "“Still,” said Château-Renaud, “Dr. dAvrigny, who attends my mother, declares he is in despair about it."
normalized = _normalize_for_pipeline(sample)
normalized = _normalize_text(sample)
assert normalized.startswith(
"“Still,” said Château-Renaud, “Doctor d'Avrigny, who attends my mother, declares he is in despair about it."
@@ -72,70 +88,100 @@ def test_normalize_roman_titles_preserves_separators() -> None:
def test_grouped_numbers_are_spelled_out() -> None:
normalized = _normalize_for_pipeline("The vault holds 35,000 credits")
normalized = _normalize_text("The vault holds 35,000 credits")
assert "thirty-five thousand" in normalized.lower()
def test_numeric_ranges_are_spoken_with_to() -> None:
normalized = _normalize_for_pipeline("Chapters 1-3")
normalized = _normalize_text("Chapters 1-3")
assert "one to three" in normalized.lower()
def test_simple_fractions_are_spoken() -> None:
normalized = _normalize_for_pipeline("Add 1/2 cup of sugar")
normalized = _normalize_text("Add 1/2 cup of sugar")
assert "one half" in normalized.lower()
def test_plain_numbers_are_spelled_out() -> None:
normalized = _normalize_for_pipeline("He rolled a 42.")
normalized = _normalize_text("He rolled a 42.")
assert "forty-two" in normalized.lower()
def test_decimal_numbers_include_point() -> None:
normalized = _normalize_for_pipeline("Book 4.5 of the series.")
normalized = _normalize_text("Book 4.5 of the series.")
assert "four point five" in normalized.lower()
def test_space_separated_numbers_become_ranges() -> None:
normalized = _normalize_for_pipeline("Read pages 12 14 tonight.")
normalized = _normalize_text("Read pages 12 14 tonight.")
assert "pages twelve to fourteen" in normalized.lower()
def test_year_like_numbers_use_common_pronunciation() -> None:
normalized = _normalize_for_pipeline("In 1924 the journey began")
normalized = _normalize_text("In 1924 the journey began")
folded = normalized.lower().replace("-", " ")
assert "nineteen twenty four" in folded
def test_early_century_years_use_hundred_format() -> None:
normalized = _normalize_for_pipeline("In 1204 the city fell")
normalized = _normalize_text("In 1204 the city fell")
assert "twelve hundred" in normalized.lower()
assert "oh four" in normalized.lower()
def test_roman_numerals_in_titles_are_converted() -> None:
normalized = _normalize_for_pipeline("Chapter IV begins now")
normalized = _normalize_text("Chapter IV begins now")
assert "chapter four" in normalized.lower()
def test_roman_numeral_suffixes_use_ordinals() -> None:
normalized = _normalize_for_pipeline("Bob Smith II arrived late")
normalized = _normalize_text("Bob Smith II arrived late")
assert "bob smith the second" in normalized.lower()
def test_lowercase_roman_after_part_converts_to_cardinal() -> None:
normalized = _normalize_text("We studied part iii of the manuscript.")
assert "part three" in normalized.lower()
def test_hyphenated_phase_with_roman_is_converted() -> None:
normalized = _normalize_text("They executed phase-IV without delay.")
assert "phase four" in normalized.lower()
def test_all_caps_quotes_are_sentence_cased() -> None:
normalized = _normalize_text('"THIS IS A TEST."')
cleaned = normalized.replace('" ', '"')
assert '"This is a test."' in cleaned
def test_caps_quote_preserves_acronyms() -> None:
normalized = _normalize_text("“THE NASA TEAM ARRIVED.”")
assert "“The NASA team arrived.”" in normalized
def test_caps_quote_normalization_respects_override() -> None:
normalized = _normalize_text(
'"KEEP SHOUTING."',
normalization_overrides={"normalization_caps_quotes": False},
)
cleaned = normalized.replace('" ', '"')
assert '"KEEP SHOUTING."' in cleaned
def test_recent_years_split_twenty_style() -> None:
normalized = _normalize_for_pipeline("In 2025 we planned ahead")
normalized = _normalize_text("In 2025 we planned ahead")
folded = normalized.lower().replace("-", " ")
assert "twenty twenty five" in folded
def test_two_thousands_use_two_thousand_prefix() -> None:
normalized = _normalize_for_pipeline("In 2005 we celebrated")
normalized = _normalize_text("In 2005 we celebrated")
assert "two thousand five" in normalized.lower()
def test_year_style_can_be_disabled() -> None:
normalized = _normalize_for_pipeline(
normalized = _normalize_text(
"In 2025 we planned ahead",
normalization_overrides={"normalization_numbers_year_style": "off"},
)
@@ -144,7 +190,7 @@ def test_year_style_can_be_disabled() -> None:
def test_contractions_can_be_kept_when_override_disabled() -> None:
normalized = _normalize_for_pipeline(
normalized = _normalize_text(
"It's a good day.",
normalization_overrides={"normalization_apostrophes_contractions": False},
)
@@ -152,7 +198,7 @@ def test_contractions_can_be_kept_when_override_disabled() -> None:
def test_sibilant_possessives_remain_when_marking_disabled() -> None:
normalized = _normalize_for_pipeline(
normalized = _normalize_text(
"The boss's chair wobbled.",
normalization_overrides={"normalization_apostrophes_sibilant_possessives": False},
)
@@ -161,7 +207,7 @@ def test_sibilant_possessives_remain_when_marking_disabled() -> None:
def test_decades_can_skip_expansion_when_disabled() -> None:
normalized = _normalize_for_pipeline(
normalized = _normalize_text(
"Classic hits from the '90s filled the hall.",
normalization_overrides={"normalization_apostrophes_decades": False},
)
@@ -170,32 +216,32 @@ def test_decades_can_skip_expansion_when_disabled() -> None:
@pytest.mark.skipif(not SPACY_RESOLVER_AVAILABLE, reason="spaCy model unavailable")
def test_spacy_disambiguates_it_has_from_context() -> None:
normalized = _normalize_for_pipeline("It's been a long time.")
normalized = _normalize_text("It's been a long time.")
assert "It has been a long time." == normalized
@pytest.mark.skipif(not SPACY_RESOLVER_AVAILABLE, reason="spaCy model unavailable")
def test_spacy_disambiguates_it_is_from_context() -> None:
normalized = _normalize_for_pipeline("It's cold outside.")
normalized = _normalize_text("It's cold outside.")
assert "It is cold outside." == normalized
@pytest.mark.skipif(not SPACY_RESOLVER_AVAILABLE, reason="spaCy model unavailable")
def test_spacy_disambiguates_she_had() -> None:
normalized = _normalize_for_pipeline("She'd left before dawn.")
normalized = _normalize_text("She'd left before dawn.")
assert "She had left before dawn." == normalized
@pytest.mark.skipif(not SPACY_RESOLVER_AVAILABLE, reason="spaCy model unavailable")
def test_spacy_disambiguates_she_would() -> None:
normalized = _normalize_for_pipeline("She'd go if invited.")
normalized = _normalize_text("She'd go if invited.")
assert "She would go if invited." == normalized
@pytest.mark.skipif(not SPACY_RESOLVER_AVAILABLE, reason="spaCy model unavailable")
def test_sample_sentence_handles_complex_contractions() -> None:
sample = "I've heard the captain'll arrive by dusk, but they'd said the same yesterday."
normalized = _normalize_for_pipeline(sample)
normalized = _normalize_text(sample)
assert (
"I have heard the captain will arrive by dusk, but they had said the same yesterday." == normalized
)
@@ -203,7 +249,7 @@ def test_sample_sentence_handles_complex_contractions() -> None:
def test_modal_will_contractions_can_be_disabled() -> None:
sample = "The captain'll arrive at dawn."
normalized = _normalize_for_pipeline(
normalized = _normalize_text(
sample,
normalization_overrides={"normalization_contraction_modal_will": False},
)