Files
Ewald a4154ccf05 Fix P0 bugs, remove Append V2/Lens Artifacts, and fix crop reload reset
- Fix batch bypass tuple handling, memory budget device-awareness, grain
  RNG device, roto payload limits, preview temp-file cleanup, and route
  double-registration.
- Add native VIDEO output (fps/audio) to effect nodes that previously
  only returned IMAGE/MASK when fed a VIDEO input.
- Split the oversized src/preview/ops/geometry.ts and add a starting
  golden-fixture test harness (spherize, transform) comparing backend
  and frontend math.
- Remove ImageOpsAppendV2 (Autogrow) and ImageOpsLensArtifacts; inline
  Append's helper back into its single caller.
- Collapse the Color Correct curve widget behind a toggle so the node
  no longer grows too tall.
- Fix Crop/generic-node video preview playback running at the RAF rate
  instead of the source video's real fps.
- Fix Crop losing its saved output size after a workflow reload:
  syncCropWidgets recomputed height from width and a preset aspect
  ratio on every resync (including onConfigure on load), discarding
  the restored value. It now only refreshes aspect-ratio bookkeeping
  on a resync.
2026-09-30 12:49:42 +02:00

133 lines
2.5 KiB
Python

from __future__ import annotations
import importlib.util
from pathlib import Path
import pytest
import torch
ROOT = Path(__file__).resolve().parents[2]
BATCH_FILE = ROOT / "nodes" / "core" / "batch.py"
spec = importlib.util.spec_from_file_location(
"imageops_batch_test_module",
BATCH_FILE,
)
assert spec is not None
assert spec.loader is not None
batch_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(batch_module)
match_batch = batch_module.match_batch
match_mask_batch = batch_module.match_mask_batch
BatchMismatchError = batch_module.BatchMismatchError
cycle_to_length = batch_module.cycle_to_length
def test_equal_batches_unchanged():
a = torch.zeros(4, 8, 8, 3)
b = torch.ones(4, 8, 8, 3)
aa, bb = match_batch(a, b)
assert aa.shape == a.shape
assert bb.shape == b.shape
def test_singleton_broadcast():
a = torch.zeros(1, 8, 8, 3)
b = torch.ones(4, 8, 8, 3)
aa, bb = match_batch(a, b)
assert aa.shape[0] == 4
assert bb.shape[0] == 4
def test_strict_mismatch_raises():
a = torch.zeros(3, 8, 8, 3)
b = torch.ones(7, 8, 8, 3)
with pytest.raises(BatchMismatchError):
match_batch(a, b)
def test_hold_last():
a = torch.arange(3).view(3, 1)
b = torch.arange(5).view(5, 1)
aa, bb = match_batch(
a,
b,
policy="hold_last",
)
assert aa[:, 0].tolist() == [0, 1, 2, 2, 2]
assert bb[:, 0].tolist() == [0, 1, 2, 3, 4]
def test_loop_is_explicit():
a = torch.arange(3).view(3, 1)
b = torch.arange(7).view(7, 1)
aa, _ = match_batch(
a,
b,
policy="loop",
)
assert aa[:, 0].tolist() == [
0, 1, 2,
0, 1, 2,
0,
]
def test_mask_singleton_broadcast():
mask = torch.ones(1, 16, 16)
result = match_mask_batch(
mask,
target_batch=4,
)
assert result.shape == (4, 16, 16)
def test_mask_mismatch_raises():
mask = torch.ones(3, 16, 16)
with pytest.raises(BatchMismatchError):
match_mask_batch(
mask,
target_batch=7,
)
def test_cycle_to_length_exact_multiple():
t = torch.arange(3).view(3, 1)
out = cycle_to_length(t, 9)
assert out[:, 0].tolist() == [0, 1, 2] * 3
def test_cycle_to_length_truncates_final_repeat():
t = torch.arange(3).view(3, 1)
out = cycle_to_length(t, 7)
assert out[:, 0].tolist() == [0, 1, 2, 0, 1, 2, 0]
def test_cycle_to_length_noop_when_already_target():
t = torch.arange(4).view(4, 1)
out = cycle_to_length(t, 4)
assert out is t