File size: 5,163 Bytes
bcbf933
3ea399a
bcbf933
 
 
 
 
 
 
 
 
 
 
 
3ea399a
 
bcbf933
 
 
 
 
 
 
 
 
 
 
 
 
 
3ea399a
bcbf933
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
"""Shared pytest fixtures and CLI flags."""

import json
import os
import pathlib
from typing import Any

import pytest

REPO_ROOT = pathlib.Path(__file__).resolve().parent.parent

DEFAULT_MASTER_WORKFLOW = pathlib.Path(
    os.environ.get(
        "LTX23_MASTER_WORKFLOW",
        pathlib.Path.home()
        / "Projects/comfyui/user/default/workflows"
        / "1. LTX 2.3 All-In-One 260406-05.json",
    )
)


def pytest_addoption(parser: pytest.Parser) -> None:
    parser.addoption("--gpu", action="store_true", help="Run L4 GPU smoke tests.")
    parser.addoption(
        "--comfy-real",
        action="store_true",
        help="Use bundled ComfyUI for L2 graph validation (slower).",
    )


def pytest_collection_modifyitems(config: pytest.Config, items: list[pytest.Item]) -> None:
    if not config.getoption("--gpu"):
        skip_gpu = pytest.mark.skip(reason="GPU smoke tests skipped (use --gpu)")
        for item in items:
            if "gpu" in item.keywords:
                item.add_marker(skip_gpu)


@pytest.fixture(scope="session")
def master_workflow() -> dict[str, Any]:
    """The full LTX 2.3 All-In-One workflow JSON (loaded from user's ComfyUI)."""
    if not DEFAULT_MASTER_WORKFLOW.exists():
        pytest.skip(
            f"Master workflow not found at {DEFAULT_MASTER_WORKFLOW}. "
            "Set LTX23_MASTER_WORKFLOW env var to its path."
        )
    return json.loads(DEFAULT_MASTER_WORKFLOW.read_text())


@pytest.fixture
def canonical_inputs() -> dict[str, dict[str, Any]]:
    """Known-good Gradio input dicts per mode (used by L1/L2 tests)."""
    return {
        "t2v": {
            "prompt": "a tiger walking through a misty forest at dawn, cinematic",
            "negative_prompt": "",
            "preset": "balanced",
            "width": 512,
            "height": 768,
            "frames": 81,
            "fps": 24,
            "seed": 42,
            "camera_lora": "none",
            "camera_strength": 0.8,
            "detailer_on": False,
            "detailer_strength": 0.5,
        },
        "i2v": {
            "prompt": "the subject turns toward the camera and smiles",
            "image": "/tmp/portrait.png",
            "preset": "balanced",
            "width": 512,
            "height": 768,
            "frames": 81,
            "fps": 24,
            "seed": 42,
            "camera_lora": "none",
            "camera_strength": 0.8,
            "detailer_on": True,
            "detailer_strength": 0.5,
            "ic_lora": "union",
            "ic_strength": 0.5,
            "pose_on": False,
        },
        "a2v": {
            "prompt": "a dancer moves to the beat in a neon-lit studio",
            "audio": "/tmp/track.wav",
            "preset": "balanced",
            "width": 512,
            "height": 768,
            "frames": 81,
            "fps": 24,
            "seed": 42,
            "audio_cfg": 7.0,
        },
        "lipsync": {
            "prompt": "the person speaks the audio with natural mouth movement",
            "image": "/tmp/portrait.png",
            "audio": "/tmp/speech.wav",
            "preset": "balanced",
            "image_strength": 0.7,
            "frames": 81,
            "fps": 24,
            "seed": 42,
        },
        "keyframe": {
            "prompt": "smooth transition between the two frames",
            "first_frame": "/tmp/start.png",
            "last_frame": "/tmp/end.png",
            "preset": "balanced",
            "frames": 81,
            "fps": 24,
            "seed": 42,
        },
        "style": {
            "prompt": "in the style of a renaissance oil painting",
            "input_video": "/tmp/source.mp4",
            "preset": "balanced",
            "frames": 81,
            "fps": 24,
            "seed": 42,
            "ic_lora": "motion-track",
            "ic_strength": 0.5,
        },
    }


@pytest.fixture
def fake_hf_cache(tmp_path: pathlib.Path) -> pathlib.Path:
    """A fake ~/.cache/huggingface/hub layout with placeholder files."""
    hub = tmp_path / "huggingface" / "hub"
    layouts = {
        "models--Lightricks--LTX-2.3": [
            "ltx-2.3-22b-distilled.safetensors",
            "ltx-2.3-spatial-upscaler-x2-1.0.safetensors",
            "ltx-2.3-22b-distilled-lora-384.safetensors",
        ],
        "models--google--gemma-3-12b-it-qat-q4_0-unquantized": [
            "model-00001-of-00005.safetensors",
            "model-00002-of-00005.safetensors",
            "model-00003-of-00005.safetensors",
            "model-00004-of-00005.safetensors",
            "model-00005-of-00005.safetensors",
            "model.safetensors.index.json",
            "tokenizer.model",
            "preprocessor_config.json",
        ],
        "models--Kijai--LTX2.3_comfy": [
            "LTX23_video_vae_bf16.safetensors",
            "LTX23_audio_vae_bf16.safetensors",
        ],
    }
    for repo, files in layouts.items():
        snapshot_dir = hub / repo / "snapshots" / "deadbeef"
        snapshot_dir.mkdir(parents=True, exist_ok=True)
        for filename in files:
            (snapshot_dir / filename).write_text("")  # placeholder
    return hub