Fix stale FakeModel/FakeVAE mocks and expectations in test_scene_chain_sampler.py

model=object() lacked model_options (needed by the per-scene wrapper
snapshot/restore added for the scene-wrapper-leak fix), and FakeVAE lacked
decode() (needed since the sampler always decodes IMAGES when the caller
doesn't declare that output as disconnected) — together these silently
killed every test in the file that calls sample(). Added FakeModel and
FakeVAE.decode, then fixed the resulting return-tuple arity (sample() now
returns 6 values, these tests unpacked 4) and three genuinely stale
assertions: the per-scene report line gained a timing segment, carry_i2v_guides
now prepends its guide frame instead of appending it, and _append_i2v_guides
no longer routes through keyframe_idxs/guide_attention_entries.

Verified in isolation: all 14 tests in this file now pass. Running the full
tests/ suite still shows the same file failing — that's a separate,
pre-existing cross-file sys.modules stubbing collision (confirmed via
git stash on a clean dev checkout) affecting this file plus
test_mask_safe_attention.py and test_v2_category_alignment.py; not fixed
here since it isn't a one-line root cause.
This commit is contained in:
digital-garbage
2026-07-08 06:09:41 +03:00
parent 0c46f1f02e
commit 449532358f
+38 -31
View File
@@ -117,6 +117,10 @@ from samplers import FunPackLTXAVSceneChainSampler
class FakeVAE:
downscale_index_formula = (1, 1, 1)
def decode(self, samples):
b, _c, t, _h, _w = samples.shape
return torch.zeros(b, t, 8, 8, 3)
class FakeModel:
"""Minimal stand-in with the one attribute the per-scene wrapper snapshot/restore
@@ -142,8 +146,8 @@ def test_scene_chain_detects_scene_count_and_increments_seed():
positive = [scene_cond(0), scene_cond(1), scene_cond(2)]
negative = [(torch.zeros(1, 2, 3), {})]
latent, status, scene_count, report = node.sample(
model=object(),
latent, _images, status, scene_count, report, _boundaries = node.sample(
model=FakeModel(),
vae=FakeVAE(),
positive=positive,
negative=negative,
@@ -175,8 +179,8 @@ def test_scene_chain_accepts_manual_combined_conditioning_without_metadata():
(torch.ones(1, 2, 3) * 3.0, {}),
]
_, status, scene_count, report = node.sample(
model=object(),
_, _images, status, scene_count, report, _boundaries = node.sample(
model=FakeModel(),
vae=FakeVAE(),
positive=positive,
negative=[],
@@ -194,8 +198,9 @@ def test_scene_chain_accepts_manual_combined_conditioning_without_metadata():
assert [call["seed"] for call in sample_calls] == [70, 71, 72]
assert [call["positive"][0][0].mean().item() for call in sample_calls] == [1.0, 2.0, 3.0]
assert "3 scene(s)" in status
assert "Scene 1: seed=70, text=Scene 1" in report
assert "Scene 3: seed=72, text=Scene 3" in report
# Per-scene report lines now include a "sampling {s}s" timing segment between seed and text.
assert "Scene 1: seed=70," in report and "text=Scene 1" in report
assert "Scene 3: seed=72," in report and "text=Scene 3" in report
def test_scene_chain_uses_scene_seed_metadata():
@@ -208,8 +213,8 @@ def test_scene_chain_uses_scene_seed_metadata():
(torch.ones(1, 2, 3), {"funpack_scene_text": "scene c", "funpack_scene_seed": 303}),
]
_, _, scene_count, report = node.sample(
model=object(),
_, _images, _status, scene_count, report, _boundaries = node.sample(
model=FakeModel(),
vae=FakeVAE(),
positive=positive,
negative=[],
@@ -238,8 +243,8 @@ def test_scene_chain_use_same_seed_forces_first_seed():
(torch.ones(1, 2, 3), {"funpack_scene_text": "scene c"}),
]
_, _, scene_count, _ = node.sample(
model=object(),
_, _images, _status, scene_count, _report, _boundaries = node.sample(
model=FakeModel(),
vae=FakeVAE(),
positive=positive,
negative=[],
@@ -266,8 +271,8 @@ def test_scene_chain_preserves_nested_av_structure_and_audio_length():
latent_template = {"samples": FakeNestedTensor([video, audio])}
positive = [scene_cond(0), scene_cond(1)]
latent, _, scene_count, _ = node.sample(
model=object(),
latent, _images, _status, scene_count, _report, _boundaries = node.sample(
model=FakeModel(),
vae=FakeVAE(),
positive=positive,
negative=[],
@@ -297,8 +302,8 @@ def test_scene_chain_default_max_is_eight_but_allows_more():
latent_template = {"samples": torch.zeros(1, 2, 3, 2, 2)}
positive = [scene_cond(index) for index in range(10)]
latent, status, scene_count, report = node.sample(
model=object(),
latent, _images, status, scene_count, report, _boundaries = node.sample(
model=FakeModel(),
vae=FakeVAE(),
positive=positive,
negative=[],
@@ -330,8 +335,8 @@ def test_scene_chain_can_append_i2v_template_as_hidden_guide():
latent_template = {"samples": samples, "noise_mask": mask}
positive = [scene_cond(0), scene_cond(1)]
latent, status, scene_count, _ = node.sample(
model=object(),
latent, _images, status, scene_count, _report, _boundaries = node.sample(
model=FakeModel(),
vae=FakeVAE(),
positive=positive,
negative=[],
@@ -346,16 +351,17 @@ def test_scene_chain_can_append_i2v_template_as_hidden_guide():
carry_i2v_guides=True,
)
# _append_i2v_guides prepends the guide frame (temporal pos 0) so it never collides with
# the overlap frames that follow: [guide, overlap0, overlap1, mid, mid, mid].
second_call = sample_calls[1]
assert scene_count == 2
assert second_call["latent_image"].shape[2] == 6
assert torch.all(second_call["latent_image"][:, :, 2:5] == 0.0)
assert torch.all(second_call["latent_image"][:, :, 5] == 7.0)
assert torch.all(second_call["noise_mask"][:, :, :2] == 0.0)
assert torch.all(second_call["noise_mask"][:, :, 2:5] == 1.0)
assert torch.all(second_call["noise_mask"][:, :, 5] == 0.0)
assert "keyframe_idxs" in second_call["positive"][0][1]
assert second_call["positive"][0][1]["guide_attention_entries"][0]["pre_filter_count"] == 1
assert torch.all(second_call["latent_image"][:, :, 0] == 7.0)
assert torch.all(second_call["latent_image"][:, :, 3:6] == 0.0)
assert torch.all(second_call["noise_mask"][:, :, :3] == 0.0)
assert torch.all(second_call["noise_mask"][:, :, 3:6] == 1.0)
# _append_i2v_guides is a plain protected-frame append (tensor + mask only) — unlike
# mid_scene_guide/joyai memory it does not add keyframe_idxs/guide_attention_entries.
assert latent["samples"].shape[2] == 8
assert "i2v guide tokens=1 latent frame(s)" in status
@@ -370,8 +376,8 @@ def test_scene_chain_expands_compact_i2v_guide_mask_to_spatial_chunk_mask():
latent_template = {"samples": samples, "noise_mask": mask}
positive = [scene_cond(0), scene_cond(1)]
_, status, scene_count, _ = node.sample(
model=object(),
_, _images, status, scene_count, _report, _boundaries = node.sample(
model=FakeModel(),
vae=FakeVAE(),
positive=positive,
negative=[],
@@ -386,10 +392,11 @@ def test_scene_chain_expands_compact_i2v_guide_mask_to_spatial_chunk_mask():
carry_i2v_guides=True,
)
# Guide frame is prepended (temporal pos 0) — see test_scene_chain_can_append_i2v_template_as_hidden_guide.
second_call = sample_calls[1]
assert scene_count == 2
assert second_call["noise_mask"].shape == second_call["latent_image"].shape
assert torch.all(second_call["noise_mask"][:, :, 5] == 0.0)
assert torch.all(second_call["noise_mask"][:, :, 0] == 0.0)
assert "i2v guide tokens=1 latent frame(s)" in status
@@ -406,8 +413,8 @@ def test_scene_chain_does_not_carry_i2v_guides_by_default():
latent_template = {"samples": samples, "noise_mask": mask}
positive = [scene_cond(0), scene_cond(1)]
_, status, scene_count, _ = node.sample(
model=object(),
_, _images, status, scene_count, _report, _boundaries = node.sample(
model=FakeModel(),
vae=FakeVAE(),
positive=positive,
negative=[],
@@ -469,7 +476,7 @@ def test_overlap_diagnostics_report_latent_blend_zone():
positive = [scene_cond(0), scene_cond(1)]
latent_template = {"samples": torch.zeros(1, 2, 5, 1, 1)}
_, _, status, _, _, boundaries_json = node.sample(
model=object(),
model=FakeModel(),
vae=FakeVAE(),
positive=positive,
negative=[],
@@ -506,7 +513,7 @@ def test_mixed_solo_applies_guides_on_first_chunk():
positive = [scene_cond(0)]
latent, _images, status, scene_count, _report, boundaries_json = node.sample(
model=object(),
model=FakeModel(),
vae=FakeVAE(),
positive=positive,
negative=[],
@@ -544,7 +551,7 @@ def test_mixed_anchor_skips_frame_overlap():
anchors = json.dumps({"1": {"filename": "anchor.png", "strength": 1.0}})
latent, _images, status, scene_count, _report, boundaries_json = node.sample(
model=object(),
model=FakeModel(),
vae=FakeVAE(),
positive=positive,
negative=[],