Files
hao-ai-lab-FastVideo/v2/tests/test_torch_backend.py
T
SolitaryThinker 1594f8e6be [fix] v2 tests: make no-torch-import guards GPU-aware (skipif torch installed)
The cuda-availability probe imports torch by design; the no-torch-import invariant is only
verifiable when torch is absent. skipif torch installed -> green on GPU box (202 passed, 2 skipped),
still enforced in torchless CI.
2026-07-04 17:15:34 +00:00

109 lines
5.1 KiB
Python

"""GPU torch backend — the CPU-verifiable tier (design_v3 §17).
The torch adapters/kernels themselves need a GPU box (see GPU_BRINGUP.md). What IS verifiable on a
torchless laptop is the wiring that must be right for the GPU path to even resolve correctly: the
cells are registered with honest sources, importing the backends never imports torch, a cuda
platform resolves the REAL cells (not a silent toy fallback), and building without torch fails loudly
rather than quietly returning a toy. Those are exactly the bugs that would otherwise surface only on
the box — so they're worth pinning here.
"""
from __future__ import annotations
import importlib.util
import sys
import pytest
from v2.card import load_card
from v2.models.wan21 import build_wan21_card
from v2.platform import (
COMPONENTS,
FLOW_MATCH_STEP,
FLOW_SDE_STEP,
KERNELS,
Platform,
component_matrix,
ensure_backends_loaded,
kernel_matrix,
)
def _make_cuda_available(monkeypatch):
"""Flip every registered cuda cell to 'available' (auto-restored), so resolution can be tested
without torch actually present."""
ensure_backends_loaded()
for reg in (*COMPONENTS._reg.values(), *KERNELS._reg.values()):
if reg.key[1] == "cuda":
monkeypatch.setattr(reg, "available", lambda: True)
# --------------------------------------------------------------------------- #
# Registration + honest sources #
# --------------------------------------------------------------------------- #
def test_cuda_has_all_three_components_and_two_solver_ops():
comps = sorted(r["kind"] for r in component_matrix() if r["device"] == "cuda")
assert comps == ["dit", "text_encoder", "vae"] # vae + text_encoder were missing before
ops = sorted(r["op"] for r in kernel_matrix() if r["device"] == "cuda")
assert ops == ["flow_match_step", "flow_sde_step"]
def test_cuda_solver_source_is_honest_no_fake_kernel():
"""fastvideo-kernel ships NO fused solver kernel — the cuda solver is plain torch. The source
must say so, not claim a 'fastvideo-kernel:flow_*' that does not exist."""
for r in kernel_matrix():
if r["device"] == "cuda":
assert "torch elementwise" in r["source"]
assert "fastvideo-kernel" not in r["source"]
assert r["arch"] == "generic" # elementwise: arch-agnostic, no static scratch
def test_cuda_cells_unavailable_on_this_box():
assert all(not r["available"] for r in component_matrix() if r["device"] == "cuda")
assert all(not r["available"] for r in kernel_matrix() if r["device"] == "cuda")
# --------------------------------------------------------------------------- #
# The load-bearing guard: importing the backends never imports torch #
# --------------------------------------------------------------------------- #
@pytest.mark.skipif(
importlib.util.find_spec("torch") is not None,
reason="torch installed (GPU box): the cuda-availability probe imports torch by design to call "
"torch.cuda.is_available(); this no-torch-import invariant is only verifiable without torch.")
def test_loading_backends_does_not_import_torch():
ensure_backends_loaded()
component_matrix(); kernel_matrix()
assert "torch" not in sys.modules
assert "v2.platform.backends.torch_adapters" not in sys.modules # imported only inside builders
assert "v2.platform.backends.torch_kernels" not in sys.modules
# --------------------------------------------------------------------------- #
# Resolution: when cuda IS available, the REAL cells win (no silent toy) #
# --------------------------------------------------------------------------- #
def test_cuda_platform_resolves_real_cells_not_toy(monkeypatch):
_make_cuda_available(monkeypatch)
p = Platform.cuda("sm90")
for kind in ("dit", "vae", "text_encoder"):
reg = COMPONENTS.resolve_first(p._component_keys(kind, "default"))
assert reg is not None and reg.key[1] == "cuda", kind # the cuda adapter, not the cpu toy
reg = p.resolve_kernel(FLOW_MATCH_STEP)
assert reg.key[1] == "cuda" and "torch elementwise" in reg.source
assert p.resolve_kernel(FLOW_SDE_STEP).key[1] == "cuda"
def test_cuda_build_fails_loudly_without_torch(monkeypatch):
"""With cuda 'available' but torch absent, building a component must RAISE (the lazy import of the
torch adapter fails) — never silently hand back a numpy toy decoding real latents."""
_make_cuda_available(monkeypatch)
inst = load_card(build_wan21_card(), cache_manager=None, platform=Platform.cuda("sm90"))
with pytest.raises((ImportError, ModuleNotFoundError, RuntimeError)):
inst.component("transformer")
# --------------------------------------------------------------------------- #
# The checkpoint seam (risk A) is additive — toy cards are unaffected #
# --------------------------------------------------------------------------- #
def test_checkpoint_field_defaults_empty_on_toy_cards():
card = build_wan21_card()
assert all(spec.checkpoint == "" for spec in card.components.values()) # CPU toys need no weights