Files
JasonHoku-ComfyUI-Ultimate-…/tests/test_preview_skip.py
T
Jason HokuandClaude Fable 5.1 602bde44fb feat(orchestrator): flat job plan, job order, preview pass and preview promotion
- run_generation_loop walks a flat job plan (config_utils.build_job_plan) instead of the
  nested resolution x config loops; job_order=minimize_reloads reorders it to cut model
  and LoRA reloads. as_written reproduces the old order exactly.
- Preview pass (fewer steps, smaller snapped latent, no upscales); items carry config_fp,
  preview_of, full_* keys. A full run skips cells whose preview was rejected (tombstoned
  in meta.rejected_preview_fps by delete_rejected); a preview run skips cells that already
  have a full image.
- check_if_job_completed gets a keyword-only preview flag (positional API unchanged).
- Distributed mode is untouched apart from an 'ignored' warning.

fix: a per-config resolution no longer leaks its w/h/batch_idx into later configs of the
same pass (the old loop set them once per input job; the pre-flight scan already did this
correctly, so pre-flight and the main loop disagreed).
fix: the async look-ahead picked the next config via job_idx*len(expanded)+conf_idx, which
was wrong whenever per-config-resolution configs were skipped after the first pass; it now
reads the next plan entry.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-11-04 00:04:13 -08:00

76 lines
2.8 KiB
Python

"""check_if_job_completed(preview=...) and tombstone collection."""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import generation_orchestrator as go # preloaded by tests/conftest.py
from config_utils import collect_rejected_preview_fps
def _conf(**kw):
c = {"model": "m", "lora_expanded": "None", "sampler": "euler", "scheduler": "normal",
"steps": 30, "cfg": 7.0, "denoise": 1.0, "seed": 5}
c.update(kw)
return c
def _item(**kw):
i = {"seed": 5, "width": 1024, "height": 1024, "batch_idx": 0, "sampler": "euler",
"scheduler": "normal", "steps": 30, "cfg": 7.0, "denoise": 1.0, "model": "m",
"positive": "p", "negative": "n", "lora": "None"}
i.update(kw)
return i
def _preview_item(**kw):
return _item(preview=True, steps=8, width=512, height=512, full_width=1024, full_height=1024,
full_steps=30, full_cfg=7.0, **kw)
def _call(items, conf, w=1024, h=1024, **kw):
return go.check_if_job_completed(items, conf, conf["seed"], w, h, 0, "p", "n", **kw)
def test_full_job_matches_full_item_not_preview():
assert _call([_item()], _conf()) == 0
assert _call([_preview_item()], _conf()) == -1
def test_preview_job_does_not_match_full_item():
assert _call([_item()], _conf(steps=8), preview=True) == -1
def test_preview_job_matches_preview_item_on_full_dims_and_preview_steps():
items = [_preview_item()]
assert _call(items, _conf(steps=8), preview=True) == 0
assert _call(items, _conf(steps=12), preview=True) == -1 # different preview steps
assert _call(items, _conf(steps=8), w=832, preview=True) == -1 # different FULL width
assert _call(items, _conf(steps=8), w=512, h=512, preview=True) == -1 # snapped dims are not the key
def test_legacy_items_without_preview_key_count_as_full():
legacy = _item()
assert "preview" not in legacy
assert _call([legacy], _conf()) == 0
def test_positional_distributed_style_call_still_works():
conf = _conf()
assert go.check_if_job_completed([_item()], conf, 5, 1024, 1024, 0, "p", "n", False) == 0
assert go.check_if_job_completed([_item()], conf, 5, 1024, 1024, 0, "p", "n") == 0
assert go.check_if_job_completed([_item()], conf, 5, 1024, 1024, 0, "p", "n", True) == -1 # optional inputs
def test_preview_is_keyword_only():
import pytest
with pytest.raises(TypeError):
go.check_if_job_completed([_item()], _conf(), 5, 1024, 1024, 0, "p", "n", False, True)
def test_collect_rejected_preview_fps():
rej = [{"preview": True, "preview_of": "a"}, {"preview": True, "preview_of": "b"},
{"rejected": True, "config_fp": "full"}, {"preview": True, "preview_of": "a"}]
assert collect_rejected_preview_fps(rej, ["z", "b"]) == ["z", "b", "a"]
assert collect_rejected_preview_fps([], None) == []