WolfDavid's picture
test(02-05): add failing analyzer behaviour tests
a09270c
Raw
History Blame
2.87 kB
"""Shared fixtures and the deployed-Space plumbing for the whole Phase 1 suite."""
from __future__ import annotations
import json
import wave
from pathlib import Path
import pytest
FIXTURES = Path(__file__).parent / "fixtures"
def pytest_addoption(parser):
parser.addoption(
"--space-url",
action="store",
default=None,
help="Base URL of the deployed Space, e.g. https://wolfdavid-japanese-learning-avatar.hf.space",
)
def pytest_collection_modifyitems(config, items):
if config.getoption("--space-url"):
return
skip = pytest.mark.skip(reason="needs --space-url (deployed Space)")
for item in items:
if "deployed" in item.keywords:
item.add_marker(skip)
@pytest.fixture(scope="session")
def space_url(request) -> str:
url = request.config.getoption("--space-url")
if not url:
pytest.skip("needs --space-url")
return url.rstrip("/")
@pytest.fixture(scope="session")
def fixtures_dir() -> Path:
return FIXTURES
@pytest.fixture(scope="session")
def silence_wav() -> Path:
return FIXTURES / "silence_30s.wav"
@pytest.fixture(scope="session")
def cafe_noise_wav() -> Path:
return FIXTURES / "cafe_noise_30s.wav"
@pytest.fixture(scope="session")
def speech_wav() -> Path:
"""Japanese speech fixture. Produced by plan 01-04 from real VOICEVOX synthesis."""
p = FIXTURES / "speech_ja.wav"
if not p.exists():
pytest.skip("speech_ja.wav not generated yet (plan 01-04)")
return p
@pytest.fixture(scope="session")
def synth_meta() -> dict:
"""Recorded synthesis metadata written by plan 01-04.
Shape: {"cases": {"<case>": {"query": "<file>.json", "wav": "<file>.wav",
"text": "...", "speed_scale": 1.0,
"duration_seconds": <float from the WAV header>,
"mora_count": <int>, "vowel_symbols": [...]}}}
"""
p = FIXTURES / "synth_meta.json"
if not p.exists():
pytest.skip("synth_meta.json not generated yet (plan 01-04)")
return json.loads(p.read_text(encoding="utf-8"))
@pytest.fixture(scope="session")
def analyzer():
"""The canonical ``analyze(text)`` (plan 02-05), warmed ONCE per session.
Warming opens the Sudachi core dictionary and gunzips the compact JMdict (the expensive
part, ~2.5-3.3 s + ~310 MB); the measured seconds are printed so the quick loop keeps
reporting what the language core costs. Yields the function, not a module, so tests read
as ``analyzer("...")``.
"""
from japanese_avatar.nlp import analyzer as mod
stats = mod.warmup()
print(f"\nanalyzer warm-up: {stats}")
yield mod.analyze
def wav_duration_seconds(path: Path) -> float:
with wave.open(str(path), "rb") as w:
return w.getnframes() / float(w.getframerate())