179 lines
5.7 KiB
Python
179 lines
5.7 KiB
Python
import hashlib
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
import torch
|
|
|
|
from sam_audio_comfy import runtime
|
|
from sam_audio_comfy.runtime import (
|
|
SpanPrompt,
|
|
discover_local_models,
|
|
ode_options,
|
|
register_model_folder,
|
|
validate_spans,
|
|
)
|
|
|
|
|
|
class FakeFolderPaths:
|
|
def __init__(self, root: Path):
|
|
self.models_dir = str(root)
|
|
self.paths = []
|
|
|
|
def add_model_folder_path(self, name, path, is_default=False):
|
|
assert name == "sam_audio"
|
|
if path not in self.paths:
|
|
self.paths.append(path)
|
|
|
|
def get_folder_paths(self, name):
|
|
assert name == "sam_audio"
|
|
return self.paths
|
|
|
|
|
|
def _make_model(path: Path):
|
|
path.mkdir(parents=True)
|
|
(path / "config.json").write_text("{}")
|
|
(path / "checkpoint.pt").write_bytes(b"checkpoint")
|
|
|
|
|
|
def test_register_and_discover_nested_local_models(tmp_path):
|
|
folders = FakeFolderPaths(tmp_path)
|
|
model_root = register_model_folder(folders)
|
|
_make_model(model_root / "custom" / "my-model")
|
|
|
|
assert discover_local_models(folders) == {
|
|
"local/custom/my-model": model_root / "custom" / "my-model"
|
|
}
|
|
|
|
|
|
def test_official_download_directories_are_not_duplicated_as_local(tmp_path):
|
|
folders = FakeFolderPaths(tmp_path)
|
|
model_root = register_model_folder(folders)
|
|
_make_model(model_root / "sam-audio-large")
|
|
|
|
assert discover_local_models(folders) == {}
|
|
|
|
|
|
def test_gated_download_uses_pinned_verified_mirror(tmp_path, monkeypatch):
|
|
class GatedRepoError(Exception):
|
|
pass
|
|
|
|
checkpoint = b"verified mirror checkpoint"
|
|
spec = runtime.MirrorSpec(
|
|
repo_id="mirror/sam-audio-small",
|
|
revision="pinned-revision",
|
|
checkpoint_size=len(checkpoint),
|
|
checkpoint_sha256=hashlib.sha256(checkpoint).hexdigest(),
|
|
)
|
|
monkeypatch.setitem(runtime.MIRROR_MODELS, "facebook/sam-audio-small", spec)
|
|
calls = []
|
|
|
|
class FakeHub:
|
|
@staticmethod
|
|
def snapshot_download(**kwargs):
|
|
calls.append(kwargs)
|
|
if kwargs["repo_id"] == "facebook/sam-audio-small":
|
|
raise GatedRepoError("access denied")
|
|
destination = Path(kwargs["local_dir"])
|
|
(destination / "config.json").write_text("{}")
|
|
(destination / "checkpoint.pt").write_bytes(checkpoint)
|
|
(destination / "LICENSE").write_text("SAM License")
|
|
return str(destination)
|
|
|
|
monkeypatch.setattr(runtime.importlib, "import_module", lambda _: FakeHub)
|
|
result = runtime._download_official_model(
|
|
"facebook/sam-audio-small", tmp_path
|
|
)
|
|
|
|
assert result == tmp_path / "sam-audio-small"
|
|
assert calls[1]["repo_id"] == spec.repo_id
|
|
assert calls[1]["revision"] == spec.revision
|
|
assert calls[1]["allow_patterns"] == [
|
|
"config.json",
|
|
"checkpoint.pt",
|
|
"LICENSE",
|
|
]
|
|
assert (result / runtime.MIRROR_MARKER).read_text() == (
|
|
f"{len(checkpoint)} {spec.checkpoint_sha256}\n"
|
|
)
|
|
|
|
|
|
def test_non_gate_download_error_does_not_use_mirror(tmp_path, monkeypatch):
|
|
calls = []
|
|
|
|
class FakeHub:
|
|
@staticmethod
|
|
def snapshot_download(**kwargs):
|
|
calls.append(kwargs)
|
|
raise ConnectionError("offline")
|
|
|
|
monkeypatch.setattr(runtime.importlib, "import_module", lambda _: FakeHub)
|
|
with pytest.raises(RuntimeError, match="Could not download"):
|
|
runtime._download_official_model("facebook/sam-audio-small", tmp_path)
|
|
|
|
assert [call["repo_id"] for call in calls] == ["facebook/sam-audio-small"]
|
|
|
|
|
|
def test_mirror_checksum_mismatch_is_rejected(tmp_path, monkeypatch):
|
|
class GatedRepoError(Exception):
|
|
pass
|
|
|
|
spec = runtime.MirrorSpec(
|
|
repo_id="mirror/sam-audio-small",
|
|
revision="pinned-revision",
|
|
checkpoint_size=3,
|
|
checkpoint_sha256=hashlib.sha256(b"good").hexdigest(),
|
|
)
|
|
monkeypatch.setitem(runtime.MIRROR_MODELS, "facebook/sam-audio-small", spec)
|
|
|
|
class FakeHub:
|
|
@staticmethod
|
|
def snapshot_download(**kwargs):
|
|
if kwargs["repo_id"] == "facebook/sam-audio-small":
|
|
raise GatedRepoError("access denied")
|
|
destination = Path(kwargs["local_dir"])
|
|
(destination / "config.json").write_text("{}")
|
|
(destination / "checkpoint.pt").write_bytes(b"bad")
|
|
(destination / "LICENSE").write_text("SAM License")
|
|
return str(destination)
|
|
|
|
monkeypatch.setattr(runtime.importlib, "import_module", lambda _: FakeHub)
|
|
with pytest.raises(RuntimeError, match="verified public mirror"):
|
|
runtime._download_official_model("facebook/sam-audio-small", tmp_path)
|
|
|
|
|
|
def test_ode_options_match_upstream_default():
|
|
assert ode_options(32) == {
|
|
"method": "midpoint",
|
|
"options": {"step_size": 2.0 / 32},
|
|
}
|
|
with pytest.raises(ValueError, match="even"):
|
|
ode_options(31)
|
|
|
|
|
|
def test_span_validation_requires_positive_and_checks_duration():
|
|
validate_spans((SpanPrompt("+", 0.25, 0.75),), duration=1.0)
|
|
|
|
with pytest.raises(ValueError, match="positive"):
|
|
validate_spans((SpanPrompt("-", 0.25, 0.75),), duration=1.0)
|
|
with pytest.raises(ValueError, match="only 1.000s"):
|
|
validate_spans((SpanPrompt("+", 0.25, 1.25),), duration=1.0)
|
|
|
|
|
|
def test_seed_context_restores_cpu_rng():
|
|
from sam_audio_comfy.runtime import seeded_inference
|
|
|
|
torch.manual_seed(123)
|
|
expected_first = torch.rand(1)
|
|
expected_second = torch.rand(1)
|
|
|
|
torch.manual_seed(123)
|
|
actual_first = torch.rand(1)
|
|
with seeded_inference(999, torch.device("cpu")):
|
|
inside_first = torch.rand(1)
|
|
actual_second = torch.rand(1)
|
|
|
|
torch.manual_seed(999)
|
|
assert torch.equal(inside_first, torch.rand(1))
|
|
assert torch.equal(actual_first, expected_first)
|
|
assert torch.equal(actual_second, expected_second)
|