[Refactor][CI] Reorganize test tree, and move slow e2e to weekly (#4951)
Signed-off-by: wangyu <410167048@qq.com>
This commit is contained in:
1 parent
925347332f
commit
7c17111ff5
158 files changed
+3479
-4130
No files matched your search
@@ -9,6 +9,9 @@ import torch
|
||||
|
||||
from vllm_omni.utils.audio import mel_filter_bank, peak_normalize
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||
|
||||
|
||||
# Parameter combinations used across the codebase.
|
||||
_PARAM_SETS = [
|
||||
# Qwen3-TTS talker / speaker encoder (sr=24000)
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
import threading
|
||||
|
||||
import pytest
|
||||
import torch
|
||||
|
||||
from vllm_omni.utils.speaker_cache import SpeakerEmbeddingCache, get_speaker_cache
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def cache():
|
||||
return SpeakerEmbeddingCache(max_bytes=10 * 1024**2)
|
||||
|
||||
|
||||
def _k(model: str, name: str, created_at: int = 0) -> tuple[str, str, int]:
|
||||
return (model, name, created_at)
|
||||
|
||||
|
||||
class TestSpeakerEmbeddingCacheBehavior:
|
||||
def test_miss_returns_none(self, cache):
|
||||
assert cache.get(_k("voxcpm2", "nonexistent")) is None
|
||||
|
||||
def test_put_and_hit(self, cache):
|
||||
cache.put(_k("voxcpm2", "alice"), {"val": 42})
|
||||
assert cache.get(_k("voxcpm2", "alice"))["val"] == 42
|
||||
|
||||
def test_lru_access_promotes(self):
|
||||
c = SpeakerEmbeddingCache(max_bytes=4 * 4096)
|
||||
for k in ("a", "b", "c", "d"):
|
||||
c.put(_k("m", k), {"emb": torch.zeros(1024, dtype=torch.float32)})
|
||||
c.get(_k("m", "a"))
|
||||
c.put(_k("m", "e"), {"emb": torch.zeros(1024, dtype=torch.float32)})
|
||||
assert c.get(_k("m", "a")) is not None
|
||||
assert c.get(_k("m", "b")) is None
|
||||
|
||||
def test_put_overwrites(self, cache):
|
||||
cache.put(_k("m", "k"), {"old": True})
|
||||
cache.put(_k("m", "k"), {"new": True})
|
||||
assert "new" in cache.get(_k("m", "k"))
|
||||
assert cache.stats()["entries"] == 1
|
||||
|
||||
def test_make_cache_key_namespaces_model_type(self):
|
||||
k1 = SpeakerEmbeddingCache.make_cache_key("alice", model_type="voxcpm2")
|
||||
k2 = SpeakerEmbeddingCache.make_cache_key("alice", model_type="fish_speech")
|
||||
assert k1 != k2
|
||||
assert k1 == ("voxcpm2", "alice", 0)
|
||||
assert k2 == ("fish_speech", "alice", 0)
|
||||
|
||||
def test_make_cache_key_created_at_isolation(self):
|
||||
k_old = SpeakerEmbeddingCache.make_cache_key("alice", model_type="voxcpm2", created_at=1712000000)
|
||||
k_new = SpeakerEmbeddingCache.make_cache_key("alice", model_type="voxcpm2", created_at=1712000042)
|
||||
assert k_old != k_new
|
||||
|
||||
def test_make_cache_key_requires_fields(self):
|
||||
with pytest.raises(ValueError):
|
||||
SpeakerEmbeddingCache.make_cache_key("", model_type="voxcpm2")
|
||||
with pytest.raises(ValueError):
|
||||
SpeakerEmbeddingCache.make_cache_key("alice", model_type="")
|
||||
|
||||
def test_clear_all(self, cache):
|
||||
cache.put(_k("m", "a"), {"v": 1})
|
||||
cache.put(_k("m", "b"), {"v": 2})
|
||||
assert cache.clear() == 2
|
||||
assert cache.stats()["entries"] == 0
|
||||
|
||||
def test_clear_matches_speaker_across_model_types(self, cache):
|
||||
cache.put(_k("voxcpm2", "alice", 1), {"v": 1})
|
||||
cache.put(_k("fish_speech", "alice", 2), {"v": 2})
|
||||
cache.put(_k("cosyvoice3", "bob", 3), {"v": 3})
|
||||
assert cache.clear("alice") == 2
|
||||
assert cache.get(_k("voxcpm2", "alice", 1)) is None
|
||||
assert cache.get(_k("fish_speech", "alice", 2)) is None
|
||||
assert cache.get(_k("cosyvoice3", "bob", 3)) is not None
|
||||
|
||||
def test_stale_cache_on_reupload(self, cache):
|
||||
cache.put(_k("voxcpm2", "alice", 1712000000), {"emb": torch.zeros(4), "gen": "old"})
|
||||
assert cache.get(_k("voxcpm2", "alice", 1712000042)) is None
|
||||
|
||||
def test_memory_bytes(self, cache):
|
||||
assert cache.memory_bytes() == 0
|
||||
t = torch.zeros(1024, dtype=torch.float32) # 4096 bytes
|
||||
cache.put(_k("m", "k"), {"emb": t})
|
||||
assert cache.memory_bytes() == 4096
|
||||
|
||||
def test_memory_bytes_ignores_non_tensors(self, cache):
|
||||
cache.put(_k("m", "k"), {"flag": True, "name": "test", "nothing": None})
|
||||
assert cache.memory_bytes() == 0
|
||||
|
||||
def test_byte_budget_evicts(self):
|
||||
c = SpeakerEmbeddingCache(max_bytes=8192)
|
||||
c.put(_k("m", "a"), {"emb": torch.zeros(1024, dtype=torch.float32)})
|
||||
c.put(_k("m", "b"), {"emb": torch.zeros(1024, dtype=torch.float32)})
|
||||
c.put(_k("m", "c"), {"emb": torch.zeros(1024, dtype=torch.float32)})
|
||||
assert c.get(_k("m", "a")) is None
|
||||
assert c.get(_k("m", "b")) is not None
|
||||
assert c.get(_k("m", "c")) is not None
|
||||
assert c.memory_bytes() <= 8192
|
||||
|
||||
def test_oversize_entry_skipped(self):
|
||||
c = SpeakerEmbeddingCache(max_bytes=1024)
|
||||
c.put(_k("m", "huge"), {"emb": torch.zeros(2048, dtype=torch.float32)})
|
||||
assert c.get(_k("m", "huge")) is None
|
||||
assert c.stats()["entries"] == 0
|
||||
|
||||
def test_stats(self, cache):
|
||||
cache.put(_k("m", "x"), {"v": 1})
|
||||
cache.get(_k("m", "x"))
|
||||
cache.get(_k("m", "y"))
|
||||
s = cache.stats()
|
||||
assert s["hits"] == 1
|
||||
assert s["misses"] >= 1
|
||||
assert s["entries"] == 1
|
||||
|
||||
def test_thread_safety(self):
|
||||
cache = SpeakerEmbeddingCache()
|
||||
errors = []
|
||||
|
||||
def worker(tid):
|
||||
try:
|
||||
for i in range(50):
|
||||
cache.put(_k("m", f"t{tid}_v{i}"), {"tid": tid})
|
||||
cache.get(_k("m", f"t{tid}_v{i}"))
|
||||
except Exception as e:
|
||||
errors.append(e)
|
||||
|
||||
threads = [threading.Thread(target=worker, args=(t,)) for t in range(10)]
|
||||
for t in threads:
|
||||
t.start()
|
||||
for t in threads:
|
||||
t.join()
|
||||
assert not errors
|
||||
assert cache.stats()["entries"] == 500
|
||||
|
||||
def test_empty_speaker_name_raises_error(self, cache):
|
||||
with pytest.raises(ValueError, match="speaker_name cannot be an empty string"):
|
||||
cache.clear("")
|
||||
|
||||
def test_cpu_storage_verification(self, cache):
|
||||
tensor = torch.randn(10, 128)
|
||||
cache.put(_k("m", "alice"), {"emb": tensor})
|
||||
cached = cache.get(_k("m", "alice"))
|
||||
assert cached["emb"].device.type == "cpu"
|
||||
|
||||
|
||||
class TestSingleton:
|
||||
def test_singleton_identity(self, fresh_speaker_cache):
|
||||
a = get_speaker_cache()
|
||||
b = get_speaker_cache()
|
||||
assert a is b
|
||||
@@ -0,0 +1,59 @@
|
||||
"""Integration tests for the process-wide speaker cache across serving + models."""
|
||||
|
||||
import pytest
|
||||
import torch
|
||||
|
||||
from vllm_omni.utils.speaker_cache import SpeakerEmbeddingCache, get_speaker_cache
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||
|
||||
|
||||
class TestSpeakerCacheIntegration:
|
||||
def test_delete_propagates_across_model_types(self):
|
||||
cache = SpeakerEmbeddingCache()
|
||||
voxcpm_key = SpeakerEmbeddingCache.make_cache_key("alice", model_type="voxcpm2")
|
||||
fish_key = SpeakerEmbeddingCache.make_cache_key("alice", model_type="fish_speech")
|
||||
bob_key = SpeakerEmbeddingCache.make_cache_key("bob", model_type="voxcpm2")
|
||||
|
||||
cache.put(voxcpm_key, {"emb": torch.zeros(4)})
|
||||
cache.put(fish_key, {"emb": torch.zeros(4)})
|
||||
cache.put(bob_key, {"emb": torch.zeros(4)})
|
||||
|
||||
removed = cache.clear("alice")
|
||||
|
||||
assert removed == 2
|
||||
assert cache.get(voxcpm_key) is None
|
||||
assert cache.get(fish_key) is None
|
||||
assert cache.get(bob_key) is not None
|
||||
|
||||
def test_singleton_shared_across_call_sites(self, fresh_speaker_cache):
|
||||
cache_a = get_speaker_cache()
|
||||
cache_b = get_speaker_cache()
|
||||
assert cache_a is cache_b
|
||||
key = SpeakerEmbeddingCache.make_cache_key("carol", model_type="voxcpm2")
|
||||
cache_a.put(key, {"tag": "from_a"})
|
||||
got = cache_b.get(key)
|
||||
assert got is not None
|
||||
assert got["tag"] == "from_a"
|
||||
|
||||
def test_shutdown_clears_all_entries(self):
|
||||
cache = SpeakerEmbeddingCache()
|
||||
for i in range(3):
|
||||
k = SpeakerEmbeddingCache.make_cache_key(f"voice{i}", model_type="voxcpm2")
|
||||
cache.put(k, {"emb": torch.zeros(2)})
|
||||
assert cache.stats()["entries"] == 3
|
||||
cache.clear()
|
||||
assert cache.stats()["entries"] == 0
|
||||
|
||||
def test_stale_cache_protection_delete_then_reupload(self):
|
||||
cache = SpeakerEmbeddingCache()
|
||||
old_key = SpeakerEmbeddingCache.make_cache_key("alice", model_type="voxcpm2", created_at=1712000000)
|
||||
cache.put(old_key, {"emb": torch.ones(4) * 3.14, "gen": "old"})
|
||||
|
||||
new_key = SpeakerEmbeddingCache.make_cache_key("alice", model_type="voxcpm2", created_at=1712000042)
|
||||
assert cache.get(new_key) is None
|
||||
assert cache.get(old_key) is not None
|
||||
|
||||
cache.clear("alice")
|
||||
assert cache.get(old_key) is None
|
||||
assert cache.get(new_key) is None
|
||||
@@ -23,6 +23,9 @@ from vllm_omni.utils.tracking_parser import (
|
||||
build_shadow_kwargs,
|
||||
)
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||
|
||||
|
||||
### Fake pipeline/deploy config for integration tests
|
||||
|
||||
_TEST_MODEL = "_test_tracking"
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
"""Unit tests for version compatibility warnings."""
|
||||
|
||||
import warnings
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
from vllm_omni.version import warn_if_misaligned_vllm_version
|
||||
|
||||
pytestmark = [pytest.mark.core_model, pytest.mark.cpu]
|
||||
|
||||
|
||||
@mock.patch("vllm_omni.version.__version_tuple__", (0, 19, 0))
|
||||
@mock.patch("vllm_omni.version.__version__", "0.19.0")
|
||||
@mock.patch("vllm.__version_tuple__", (0, 18, 0))
|
||||
@mock.patch("vllm.__version__", "0.18.0")
|
||||
def test_version_mismatch_warning():
|
||||
"""Ensure that we warn when vLLM and vLLM-Omni major/minor versions differ."""
|
||||
with pytest.warns(RuntimeWarning, match="mismatched major/minor versions"):
|
||||
warn_if_misaligned_vllm_version()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"vllm_ver,vllm_tuple,omni_ver,omni_tuple",
|
||||
[
|
||||
("0.19.0", (0, 19, 0), "0.19.5", (0, 19, 5)), # Patch differs
|
||||
("0.18.0", (0, 18, 0), "dev", (0, 0, "dev")), # Omni dev
|
||||
("dev", (0, 0, "dev"), "0.19.0", (0, 19, 0)), # vLLM dev
|
||||
# Ensure local identifies don't matter for the warning
|
||||
("0.19.0+foo", (0, 19, 0, "foo"), "0.19.5", (0, 19, 0)),
|
||||
("0.19.0", (0, 19, 0), "0.19.5+bar", (0, 19, 0, "bar")),
|
||||
("0.19.0+foo", (0, 19, 0, "foo"), "0.19.5+bar", (0, 19, 0, "bar")),
|
||||
],
|
||||
)
|
||||
def test_no_warning_cases(vllm_ver, vllm_tuple, omni_ver, omni_tuple):
|
||||
"""Ensure we don't warn when minor versions match or either is a dev build."""
|
||||
with (
|
||||
mock.patch.multiple("vllm", __version__=vllm_ver, __version_tuple__=vllm_tuple),
|
||||
mock.patch.multiple("vllm_omni.version", __version__=omni_ver, __version_tuple__=omni_tuple),
|
||||
):
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("error")
|
||||
warn_if_misaligned_vllm_version()
|
||||
|
||||
|
||||
@mock.patch("vllm_omni.version.__version_tuple__", (0, 19, 0))
|
||||
@mock.patch("vllm_omni.version.__version__", "0.19.0rc2.dev21")
|
||||
@mock.patch("vllm.__version_tuple__", (0, 18, 0))
|
||||
@mock.patch("vllm.__version__", "0.18.0")
|
||||
def test_warning_contains_version_strings():
|
||||
"""Ensure that the warning contains the full version strings."""
|
||||
with pytest.warns(RuntimeWarning) as record:
|
||||
warn_if_misaligned_vllm_version()
|
||||
|
||||
assert len(record) == 1
|
||||
msg = str(record[0].message)
|
||||
assert "0.19.0rc2.dev21" in msg
|
||||
assert "0.18.0" in msg
|
||||
|
||||
|
||||
def test_omni_plugin_loader_invokes_vllm_general_plugins_first(monkeypatch):
|
||||
import vllm.plugins as vllm_plugins
|
||||
|
||||
import vllm_omni.plugins as omni_plugins
|
||||
|
||||
calls = []
|
||||
monkeypatch.setattr(omni_plugins, "omni_plugins_loaded", False)
|
||||
monkeypatch.setattr(vllm_plugins, "load_general_plugins", lambda: calls.append("vllm"))
|
||||
monkeypatch.setattr(
|
||||
omni_plugins,
|
||||
"load_omni_plugins_by_group",
|
||||
lambda group: {"omni": lambda: calls.append(group)},
|
||||
)
|
||||
|
||||
omni_plugins.load_omni_general_plugins()
|
||||
|
||||
assert calls == ["vllm", omni_plugins.OMNI_DEFAULT_PLUGINS_GROUP]
|
||||
|
||||
omni_plugins.load_omni_general_plugins()
|
||||
|
||||
assert calls == ["vllm", omni_plugins.OMNI_DEFAULT_PLUGINS_GROUP]
|
||||
Reference in new issue
Block a user