Files
olivv-cs-ComfyUI-FunPack/tests/test_run_phase.py
T

92 lines
3.8 KiB
Python

"""run_phase — the live "what is the sampler doing" label behind the progress readout.
The one invariant that actually breaks in production: the WRITER (samplers.py) reaches this
module as a package-relative import and the READER (movie_editor's bridge) imports FunPack
modules top-level by name, so the two can hold different module objects. State in module
globals would be two separate copies and the label would never arrive. It lives on `sys`
for exactly that reason, and this pins it down.
"""
import importlib.util
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT))
import run_phase # noqa: E402
def _second_import():
"""A second, independent module object for the same file — what the other import path
produces at runtime."""
spec = importlib.util.spec_from_file_location("other_pkg.run_phase", ROOT / "run_phase.py")
mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mod)
return mod
def test_both_import_paths_see_the_same_label():
other = _second_import()
run_phase.set_phase("scene 2/3 · pass 2 of 2")
assert other.current()["label"] == "scene 2/3 · pass 2 of 2"
other.clear()
assert run_phase.current()["label"] == ""
def test_seq_only_ever_increases():
"""A poller uses it to tell "still the same phase" from "this phase came round again",
so it must never reset — not even when the label is cleared."""
before = run_phase.current()["seq"]
run_phase.set_phase("a")
run_phase.set_phase("a")
run_phase.clear()
assert run_phase.current()["seq"] == before + 3
def test_a_bad_label_never_raises():
"""This is a readout on the sampling path: it must not be able to fail a render."""
run_phase.set_phase(None)
assert run_phase.current()["label"] == ""
run_phase.set_phase(12)
assert run_phase.current()["label"] == "12"
run_phase.clear()
def test_the_bridge_reads_the_same_sys_key_without_importing_this_module():
"""movie_editor's progress poll runs every 700ms on ComfyUI's event loop WHILE the
worker thread samples, so it reads the label straight off `sys` rather than importing
anything. That makes the key name a contract between two files — pin it."""
src = (ROOT / "movie_editor" / "backend" / "bridge.py").read_text()
assert run_phase._SYS_KEY == "_funpack_run_phase"
assert f'getattr(sys, "{run_phase._SYS_KEY}"' in src
# ...and the poll must not have grown an import back.
poll = src.split("def current_progress(")[1].split("\ndef ")[0]
assert "import_module" not in poll and "_funpack_attr" not in poll
# ── live prompt-enhancer readout ─────────────────────────────────────────────
def _on_run(monkeypatch, run_id):
import server
monkeypatch.setattr(server.PromptServer, "instance",
type("S", (), {"last_prompt_id": run_id})(), raising=False)
def test_rewrites_are_published_live_and_tagged_with_their_run(monkeypatch):
other = _second_import()
_on_run(monkeypatch, "run-1")
run_phase.reset_enhanced()
run_phase.publish_enhanced({"scene": 0, "after": "A"})
other.publish_enhanced({"scene": 1, "after": "B"}) # the other import path adds to it
live = sys._funpack_run_phase["enhanced"]
assert live["prompt_id"] == "run-1" and [i["after"] for i in live["items"]] == ["A", "B"]
def test_a_new_run_never_carries_the_last_runs_rewrite(monkeypatch):
_on_run(monkeypatch, "run-1")
run_phase.publish_enhanced({"after": "old"})
_on_run(monkeypatch, "run-2")
run_phase.publish_enhanced({"after": "new"})
live = sys._funpack_run_phase["enhanced"]
assert live["prompt_id"] == "run-2" and [i["after"] for i in live["items"]] == ["new"]