Files
wildminder-ComfyUI-Chatterbox/tests/test_vendor_loader.py
T

275 lines
9.9 KiB
Python

"""S2.1 -- ``ensure_vendor_on_path`` appends ``src/``, once, at the tail.
The vendored ``chatterbox`` tree is not a package ComfyUI puts on ``sys.path``,
so the loader appends this repository's ``src/``. Two properties matter and both
are easy to get wrong:
* **append, never insert** -- a prepend would silently shadow a pip-installed
``chatterbox`` the user chose, and would shadow site-packages generally;
* **idempotent** -- ComfyUI re-imports a custom node on every startup, so a
loader that appends unconditionally grows ``sys.path`` forever.
The in-process tests run the real function. The end-to-end check needs a clean
interpreter (the harness mocks the whole vendor tree), so it runs in a
subprocess and *skips with a named reason* when a dependency is genuinely
missing -- never silently, and never by swallowing the error.
"""
from __future__ import annotations
import json
import os
import subprocess
import sys
import textwrap
import pytest
from conftest import ROOT_DIR
SRC_DIR = os.path.join(ROOT_DIR, "src")
MODULES_DIR = os.path.join(ROOT_DIR, "modules")
@pytest.fixture
def vendor_loader():
"""The loader module, imported by file path and restored afterwards.
``modules`` is a real package in the repository, but under the pytest alias
the harness registers it as a namespace stand-in; loading the file directly
keeps this test honest about which function it exercises.
"""
import importlib.util
path = os.path.join(MODULES_DIR, "vendor_loader.py")
spec = importlib.util.spec_from_file_location("_vendor_loader_under_test", path)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
@pytest.fixture
def clean_sys_path():
"""``sys.path`` without our ``src/``, restored however the test ends."""
original = list(sys.path)
try:
while SRC_DIR in sys.path:
sys.path.remove(SRC_DIR)
yield
finally:
sys.path[:] = original
class TestAppendSemantics:
def test_src_lands_at_the_tail(self, vendor_loader, clean_sys_path) -> None:
vendor_loader.ensure_vendor_on_path()
assert sys.path[-1] == SRC_DIR, (
"the vendored src/ must be appended, so anything already importable "
"keeps winning"
)
def test_nothing_is_inserted_ahead_of_the_existing_entries(
self, vendor_loader, clean_sys_path
) -> None:
before = list(sys.path)
vendor_loader.ensure_vendor_on_path()
assert sys.path[: len(before)] == before, (
"an existing entry was reordered; a pip-installed chatterbox or "
"site-packages entry has to keep winning"
)
assert sys.path[len(before):] == [SRC_DIR], (
f"expected exactly one appended entry, got {sys.path[len(before):]}"
)
def test_idempotent_across_repeated_calls(self, vendor_loader, clean_sys_path) -> None:
vendor_loader.ensure_vendor_on_path()
after_first = list(sys.path)
for _ in range(5):
vendor_loader.ensure_vendor_on_path()
assert sys.path == after_first, (
f"repeated calls grew sys.path: {len(after_first)} -> {len(sys.path)}"
)
assert sys.path.count(SRC_DIR) == 1
def test_call_on_an_already_present_path_is_a_no_op(
self, vendor_loader, clean_sys_path
) -> None:
vendor_loader.ensure_vendor_on_path()
# A pip-installed chatterbox sits in front of us; that must stay true.
sys.path.insert(0, "/some/site-packages")
before = list(sys.path)
vendor_loader.ensure_vendor_on_path()
assert sys.path == before
assert sys.path[0] == "/some/site-packages"
def test_returns_none(self, vendor_loader, clean_sys_path) -> None:
assert vendor_loader.ensure_vendor_on_path() is None
class TestLoaderPackage:
def test_modules_is_a_real_package(self) -> None:
init = os.path.join(MODULES_DIR, "__init__.py")
assert os.path.isfile(init), (
"modules/ has no __init__.py; `from .modules.vendor_loader import ...` "
"cannot resolve without it"
)
def test_modules_init_is_empty(self) -> None:
"""An empty ``__init__.py``: re-exporting would drag the handler in."""
init = os.path.join(MODULES_DIR, "__init__.py")
with open(init, "r", encoding="utf-8") as handle:
assert handle.read().strip() == "", "modules/__init__.py must stay empty"
def test_loader_exports_exactly_one_function(self, vendor_loader) -> None:
assert vendor_loader.__all__ == ["ensure_vendor_on_path"]
assert callable(vendor_loader.ensure_vendor_on_path)
def test_no_insert_call_anywhere_in_the_loader(self, vendor_loader) -> None:
with open(
os.path.join(MODULES_DIR, "vendor_loader.py"), "r", encoding="utf-8"
) as handle:
source = handle.read()
assert "sys.path.insert" not in source, (
"the loader must never prepend; a pip-installed chatterbox has to win"
)
def test_repo_entry_point_no_longer_inserts_src(self) -> None:
"""S2.4 removed the ``sys.path.insert(0, src_dir)`` from ``__init__.py``."""
with open(os.path.join(ROOT_DIR, "__init__.py"), "r", encoding="utf-8") as handle:
source = handle.read()
assert "sys.path.insert" not in source, (
"the entry point still prepends src/ to sys.path"
)
assert "ensure_vendor_on_path()" in source
# ---------------------------------------------------------------------------
# End to end, in a clean interpreter
# ---------------------------------------------------------------------------
PROBE = textwrap.dedent(
"""
import json, sys
sys.path.append({modules_dir!r})
from vendor_loader import ensure_vendor_on_path
before = list(sys.path)
ensure_vendor_on_path()
delta = [entry for entry in sys.path if entry not in before]
result = {{
'added': delta,
'is_tail': sys.path[-1] == {src_dir!r},
'index': sys.path.index({src_dir!r}),
'count': sys.path.count({src_dir!r}),
}}
try:
from chatterbox.tts import ChatterboxTTS
from chatterbox.vc import ChatterboxVC
from chatterbox.mtl_tts import ChatterboxMultilingualTTS
from chatterbox.tts_turbo import ChatterboxTurboTTS
except BaseException as exc:
result['import_kind'] = type(exc).__name__
result['import_detail'] = str(exc)[:300]
result['missing'] = getattr(exc, 'name', None)
else:
result['import_kind'] = 'ok'
result['classes'] = [
ChatterboxTTS.__name__, ChatterboxVC.__name__,
ChatterboxMultilingualTTS.__name__, ChatterboxTurboTTS.__name__,
]
result['perth_imported'] = 'perth' in sys.modules
result['diffusers_imported'] = 'diffusers' in sys.modules
print('__PROBE__' + json.dumps(result))
"""
)
def run_probe() -> dict:
script = PROBE.format(modules_dir=MODULES_DIR, src_dir=SRC_DIR)
completed = subprocess.run(
[sys.executable, "-c", script],
cwd=ROOT_DIR,
capture_output=True,
text=True,
timeout=900,
)
for line in completed.stdout.splitlines():
if line.startswith("__PROBE__"):
return json.loads(line[len("__PROBE__"):])
pytest.fail(
"the vendor-loader probe produced no result\n"
f"--- stdout ---\n{completed.stdout[-2000:]}\n"
f"--- stderr ---\n{completed.stderr[-2000:]}"
)
@pytest.fixture(scope="module")
def probe() -> dict:
return run_probe()
#: Third-party modules the vendored tree legitimately needs. If one of these is
#: absent the probe skips -- by name, so "why did this not run?" is answerable.
KNOWN_EXTERNAL_REQUIREMENTS = (
"librosa",
"torchaudio",
"transformers",
"s3tokenizer",
"conformer",
"huggingface_hub",
"safetensors",
"einops",
"pyloudnorm",
)
class TestRealVendorResolvesAfterTheLoaderRuns:
def test_loader_appends_exactly_one_entry_at_the_tail(self, probe: dict) -> None:
assert probe["added"] == [SRC_DIR], (
f"the loader added {probe['added']!r}; it must add src/ and nothing else"
)
assert probe["is_tail"] is True
assert probe["count"] == 1
def test_vendor_entry_points_import(self, probe: dict) -> None:
if probe["import_kind"] != "ok":
pytest.skip(
"vendored chatterbox does not import in this environment: "
f"{probe['import_kind']} on {probe.get('missing')!r} "
f"({probe.get('import_detail')}); install "
f"{', '.join(KNOWN_EXTERNAL_REQUIREMENTS)} to run this check"
)
assert set(probe["classes"]) == {
"ChatterboxTTS",
"ChatterboxVC",
"ChatterboxMultilingualTTS",
"ChatterboxTurboTTS",
}
def test_import_does_not_drag_perth_or_diffusers_in(self, probe: dict) -> None:
if probe["import_kind"] != "ok":
pytest.skip(
"vendored chatterbox does not import in this environment "
f"({probe['import_kind']}: {probe.get('missing')})"
)
assert probe["perth_imported"] is False, (
"perth is an optional dependency; importing chatterbox must not need it"
)
assert probe["diffusers_imported"] is False, (
"diffusers is no longer a dependency of the vendored tree"
)
def test_a_missing_dependency_is_never_reported_as_success(self, probe: dict) -> None:
"""A vacuous pass is the failure mode this guards against."""
if probe["import_kind"] != "ok":
assert probe["import_kind"] in {"ModuleNotFoundError", "ImportError"}, (
f"import chatterbox failed on {probe['import_kind']}: "
f"{probe.get('import_detail')}"
)
assert probe.get("missing"), "the failure names no missing module"