4danyone-rerun / tests /test_pipeline_lifecycle.py
pablovela5620's picture
Claude Fable 5
Apply the thermo-review survivors (app side)
260a292
Raw History Blame Contribute Delete
3.84 kB
"""Scratch-lifecycle tests: ``generate_run`` consumes its ``PreparedRun``.
The cleanup guarantee used to live in every caller; these tests pin it to the
one owner. Settlement and discard must both happen exactly once whether
generation succeeds, generation raises (including a cancellation hook), or
settlement itself raises.
"""
from __future__ import annotations
import os
from fractions import Fraction
from pathlib import Path
import pytest
os.environ.setdefault("FDANYONE_SKIP_LOAD", "1")
import fdanyone.model.inference
import fdanyone.output
from fdanyone.pipeline import PreparedRun, generate_run # noqa: E402
class _Conditioning:
"""Stands in for target conditioning with a deferred skeleton render."""
def __init__(self, settlement_error: Exception | None = None) -> None:
self.target_waiter: object | None = object()
self.settlement_error: Exception | None = settlement_error
self.settled: int = 0
def wait_for_target_skeletons(self) -> None:
self.settled += 1
if self.settlement_error is not None:
raise self.settlement_error
def _prepared(tmp_path: Path, conditioning: _Conditioning) -> PreparedRun:
scratch: Path = tmp_path / "scratch"
scratch.mkdir()
(scratch / "intermediate.bin").write_bytes(b"scratch")
return PreparedRun(
settings=object(),
clip=object(),
motion=object(),
conditioning=conditioning,
checkpoint=tmp_path / "checkpoint.safetensors",
base_assets=object(),
prompt_embedding_path=None,
model_identity={},
device="cpu",
scratch=scratch,
motion_dir=tmp_path / "motion",
result_dir=tmp_path / "result",
pipeline_started=0.0,
)
def test_generate_run_settles_and_discards_on_success(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
conditioning: _Conditioning = _Conditioning()
prepared: PreparedRun = _prepared(tmp_path, conditioning)
monkeypatch.setattr(fdanyone.model.inference, "generate_views", lambda **_: object())
monkeypatch.setattr(
fdanyone.output,
"export_result",
lambda **_: {"fps": Fraction(25, 1), "total_pipeline_elapsed_seconds": 1.5},
)
result = generate_run(prepared, seed=0)
assert result.result_dir == prepared.result_dir
assert result.total_elapsed_seconds == 1.5
assert result.metadata["result_dir"] == str(prepared.result_dir)
assert conditioning.settled == 1
assert not prepared.scratch.exists()
def test_generate_run_settles_and_discards_when_generation_raises(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
conditioning: _Conditioning = _Conditioning()
prepared: PreparedRun = _prepared(tmp_path, conditioning)
def explode(**_: object) -> object:
raise RuntimeError("cancelled mid-denoise")
monkeypatch.setattr(fdanyone.model.inference, "generate_views", explode)
with pytest.raises(RuntimeError, match="cancelled mid-denoise"):
generate_run(prepared, seed=0)
assert conditioning.settled == 1
assert not prepared.scratch.exists()
def test_generate_run_discards_even_when_settlement_raises(
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
conditioning: _Conditioning = _Conditioning(settlement_error=RuntimeError("render worker died"))
prepared: PreparedRun = _prepared(tmp_path, conditioning)
monkeypatch.setattr(fdanyone.model.inference, "generate_views", lambda **_: object())
monkeypatch.setattr(
fdanyone.output,
"export_result",
lambda **_: {"total_pipeline_elapsed_seconds": 0.0},
)
with pytest.raises(RuntimeError, match="render worker died"):
generate_run(prepared, seed=0)
assert conditioning.settled == 1
assert not prepared.scratch.exists()