Files
wehub-resource-sync 1b8708893a
Security Scan / tests (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:12:26 +08:00

219 lines
7.4 KiB
Python

# SPDX-License-Identifier: MIT
import importlib.util
import json
import os
import sys
import tempfile
import unittest
BACKEND_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, BACKEND_DIR)
# longcat-video is a backend directory, not an importable Python package name.
from longcat_utils import ( # noqa: E402
MODEL_KIND_AVATAR,
MODEL_KIND_BASE,
attention_overrides,
avatar_segments_for_duration,
avatar_segments_for_frames,
classify_model,
normalize_model_source,
normalize_num_frames,
parse_options,
validate_dimensions,
)
SOURCE_DIR = os.path.join(BACKEND_DIR, "sources", "LongCat-Video")
try:
import torch
sys.path.insert(0, SOURCE_DIR)
ATTENTION_TESTS_AVAILABLE = (
os.path.isdir(SOURCE_DIR) and importlib.util.find_spec("triton") is not None
)
except ImportError:
torch = None
ATTENTION_TESTS_AVAILABLE = False
AVATAR_ATTENTION_TESTS_AVAILABLE = ATTENTION_TESTS_AVAILABLE and all(
importlib.util.find_spec(module) is not None
for module in ("pyloudnorm", "scipy", "torchvision")
)
class LongCatUtilsTest(unittest.TestCase):
def test_parse_options_preserves_colons_and_coerces_scalars(self):
options = parse_options(
[
"use_distill:true",
"max_segments:4",
"audio_guidance_scale:3.5",
"source:https://example.com/model",
"flag",
]
)
self.assertEqual(options["use_distill"], True)
self.assertEqual(options["max_segments"], 4)
self.assertEqual(options["audio_guidance_scale"], 3.5)
self.assertEqual(options["source"], "https://example.com/model")
self.assertEqual(options["flag"], True)
def test_classify_model_accepts_only_supported_longcat_models(self):
cases = {
"meituan-longcat/LongCat-Video": MODEL_KIND_BASE,
"https://huggingface.co/meituan-longcat/LongCat-Video": MODEL_KIND_BASE,
"hf://meituan-longcat/LongCat-Video-Avatar-1.5": MODEL_KIND_AVATAR,
"other-org/LongCat-Video": None,
"meituan-longcat/LongCat-Video-Avatar": None,
"some-org/unrelated-model": None,
}
for model, expected in cases.items():
with self.subTest(model=model):
self.assertEqual(classify_model(model), expected)
def test_classify_model_reads_local_checkpoint_metadata(self):
with tempfile.TemporaryDirectory() as directory:
with open(
os.path.join(directory, "model_index.json"),
"w",
encoding="utf-8",
) as config_file:
json.dump({"model_name": "LongCat-Video-Avatar-1.5"}, config_file)
self.assertEqual(classify_model(directory), MODEL_KIND_AVATAR)
def test_normalize_model_source_handles_huggingface_uri_forms(self):
self.assertEqual(
normalize_model_source(
"https://huggingface.co/meituan-longcat/LongCat-Video/tree/main"
),
"meituan-longcat/LongCat-Video",
)
self.assertEqual(
normalize_model_source("huggingface://meituan-longcat/LongCat-Video"),
"meituan-longcat/LongCat-Video",
)
def test_frame_and_segment_rounding_matches_longcat_temporal_shape(self):
self.assertEqual(normalize_num_frames(94), 93)
self.assertEqual(normalize_num_frames(0), 93)
self.assertEqual(avatar_segments_for_frames(93), 1)
self.assertEqual(avatar_segments_for_frames(94), 2)
self.assertEqual(avatar_segments_for_frames(173), 2)
self.assertEqual(avatar_segments_for_frames(174), 3)
self.assertEqual(avatar_segments_for_duration(10.0), 3)
def test_dimensions_are_bounded_and_aligned(self):
self.assertEqual(validate_dimensions(0, 0), (832, 480))
self.assertEqual(validate_dimensions(512, 512), (512, 512))
with self.assertRaisesRegex(ValueError, "divisible by 16"):
validate_dimensions(513, 512)
with self.assertRaisesRegex(ValueError, "must not exceed"):
validate_dimensions(1920, 1080)
def test_attention_backend_validation(self):
self.assertEqual(
attention_overrides("sdpa"),
{
"enable_flashattn2": False,
"enable_flashattn3": False,
"enable_xformers": False,
},
)
with self.assertRaisesRegex(ValueError, "attention_backend"):
attention_overrides("unknown")
@unittest.skipUnless(
ATTENTION_TESTS_AVAILABLE,
"patched LongCat source and torch are required for attention tests",
)
class SDPAFallbackTest(unittest.TestCase):
def test_base_self_attention_matches_reference(self):
from longcat_video.modules.attention import Attention
dim, heads, sequence = 64, 4, 32
attention = Attention(
dim,
heads,
enable_flashattn2=False,
enable_flashattn3=False,
enable_xformers=False,
enable_bsa=False,
).float()
query = torch.randn(2, heads, sequence, dim // heads)
key = torch.randn_like(query)
value = torch.randn_like(query)
output = attention._process_attn(query, key, value, shape=(1, 1, sequence))
reference = (
torch.softmax(
(query @ key.transpose(-1, -2)) * attention.scale,
dim=-1,
)
@ value
)
self.assertLess((output - reference).abs().max().item(), 1e-4)
@unittest.skipUnless(
AVATAR_ATTENTION_TESTS_AVAILABLE,
"avatar audio dependencies are required for the avatar attention test",
)
def test_avatar_self_attention_matches_reference(self):
from longcat_video.modules.avatar.attention import Attention
dim, heads, sequence = 64, 4, 16
attention = Attention(
dim,
heads,
enable_flashattn2=False,
enable_flashattn3=False,
enable_xformers=False,
).float()
query = torch.randn(1, heads, sequence, dim // heads)
key = torch.randn_like(query)
value = torch.randn_like(query)
output = attention._process_attn(query, key, value, shape=(1, 1, sequence))
reference = (
torch.softmax(
(query @ key.transpose(-1, -2)) * attention.scale,
dim=-1,
)
@ value
)
self.assertLess((output - reference).abs().max().item(), 1e-4)
def test_base_cross_attention_remains_block_diagonal(self):
from longcat_video.modules.attention import MultiHeadCrossAttention
dim, heads = 64, 4
attention = MultiHeadCrossAttention(
dim,
heads,
enable_flashattn2=False,
enable_flashattn3=False,
enable_xformers=False,
).float()
query = torch.randn(2, 8, dim)
key_lengths = [5, 7]
condition = torch.randn(1, sum(key_lengths), dim)
first = attention._process_cross_attn(query, condition, key_lengths)
changed = condition.clone()
changed[:, key_lengths[0] :] = torch.randn_like(changed[:, key_lengths[0] :])
second = attention._process_cross_attn(query, changed, key_lengths)
self.assertLess((first[0] - second[0]).abs().max().item(), 1e-5)
self.assertGreater((first[1] - second[1]).abs().max().item(), 1e-3)
if __name__ == "__main__":
unittest.main()