Files
2026-07-15 23:41:04 +02:00

137 lines
5.7 KiB
Python

# Tests for the comfy-free resolution core — part of ComfyUI-AI2Go-Utils. GPL-3.0.
from nodes.resolution_core import (
resolve_dims, effective_ar, parse_ar, profile_clamps, aspect_options,
clamp_snap_multiple,
)
SQ = "1:1 (Square)"
WS = "16:9 (Widescreen)"
def test_default_profile_never_clamps():
assert resolve_dims("default", "raw", SQ, "landscape", 8, 1.0, 5000, 5000) == (5000, 5000)
def test_snap_multiple_rounds_to_8():
# 1021/8 = 127.625 -> 128 -> 1024 ; 1000 already a multiple of 8
assert resolve_dims("default", "raw", SQ, "landscape", 8, 1.0, 1021, 1000) == (1024, 1000)
def test_snap_multiple_is_configurable():
# 1030/64 = 16.09 -> 16 -> 1024
assert resolve_dims("default", "raw", SQ, "landscape", 64, 1.0, 1030, 1030) == (1024, 1024)
def test_ideogram_ignores_snap_multiple():
# snap_multiple=64 is ignored; Ideogram stays mult 16: 1050/16 = 65.625 -> 66 -> 1056
assert resolve_dims("Ideogram 4", "raw", SQ, "landscape", 64, 1.0, 1050, 1050) == (1056, 1056)
def test_default_uses_snap_multiple_not_model_mult():
# same input as above but default profile + mult 64 -> 1024, proving the two profiles differ
assert resolve_dims("default", "raw", SQ, "landscape", 64, 1.0, 1050, 1050) == (1024, 1024)
def test_ideogram_clamps_16_9_keeping_aspect():
assert resolve_dims("Ideogram 4", "auto", WS, "landscape", 8, 1.0, 4000, 0) == (2048, 1152)
def test_orientation_transpose_portrait_is_taller():
w, h = resolve_dims("default", "auto", WS, "portrait", 8, 1.0, 1080, 0)
assert h > w and (w, h) == (1080, 1920)
def test_orientation_landscape_is_wider():
w, h = resolve_dims("default", "auto", WS, "landscape", 8, 1.0, 1920, 0)
assert w > h and (w, h) == (1920, 1080)
def test_effective_ar_inverts_for_portrait():
assert round(effective_ar(WS, "landscape"), 4) == round(16 / 9, 4)
assert round(effective_ar(WS, "portrait"), 4) == round(9 / 16, 4)
def test_backward_compat_bare_ratio_matches_label():
bare = resolve_dims("default", "auto", "16:9", "landscape", 8, 1.0, 1920, 0)
label = resolve_dims("default", "auto", WS, "landscape", 8, 1.0, 1920, 0)
assert bare == label == (1920, 1080)
assert parse_ar("16:9") == parse_ar(WS)
def test_new_21_9_ratio():
# 21:9 ; 2016 is a multiple of 8 and 2016*9/21 = 864 (also /8) -> no rounding ambiguity
assert resolve_dims("default", "auto", "21:9 (Cinemascope)", "landscape", 8, 1.0, 2016, 0) == (2016, 864)
def test_profile_clamps_flag():
assert profile_clamps("Ideogram 4") is True
assert profile_clamps("default") is False
def test_aspect_options_shape():
opts = aspect_options()
assert opts[0] == "1:1 (Square)"
assert "16:9 (Widescreen)" in opts
assert "21:9 (Cinemascope)" in opts
assert len(opts) == 9
# square + landscape only — no portrait ratios listed
assert not any(lbl.startswith(("9:16", "2:3", "3:4", "4:5", "1:2", "9:21", "1:3")) for lbl in opts)
def test_megapixel_mode_hits_target_and_ratio():
# megapixel: default profile, 16:9, 2.0 MP -> ~2 MP at ~16:9, snapped to /8
w, h = resolve_dims("default", "megapixel", WS, "landscape", 8, 2.0, 0, 0)
assert (w, h) == (1888, 1064)
def test_backward_compat_portrait_bare_ratio():
# old workflow bare "9:16" with orientation defaulting to landscape still yields portrait dims
w, h = resolve_dims("default", "auto", "9:16", "landscape", 8, 1.0, 1080, 0)
assert h > w and (w, h) == (1080, 1920)
def test_flipped_9_21_portrait():
# 21:9 flipped to portrait (9:21) -> tall; 864 and 2016 are both multiples of 8
w, h = resolve_dims("default", "auto", "21:9 (Cinemascope)", "portrait", 8, 1.0, 864, 0)
assert h > w and (w, h) == (864, 2016)
# ── snap_multiple robustness (regression: an emptied number widget serialized "" -> the node's
# INT validation rejected the whole prompt, blocking even the Ideogram profile that ignores it). ──
def test_clamp_snap_multiple_heals_bad_values_to_default():
# Anything non-numeric / < 1 heals to the default 8 so a stray value can never break a profile.
assert clamp_snap_multiple("") == 8 # the empty string an emptied number widget serializes
assert clamp_snap_multiple(0) == 8
assert clamp_snap_multiple(None) == 8
assert clamp_snap_multiple(-5) == 8
assert clamp_snap_multiple("abc") == 8
def test_clamp_snap_multiple_keeps_and_bounds_valid_values():
assert clamp_snap_multiple(16) == 16
assert clamp_snap_multiple("64") == 64 # numeric strings accepted (widget can hand back a string)
assert clamp_snap_multiple(8.9) == 8 # truncated to int
assert clamp_snap_multiple(3000) == 1024 # clamped to the widget max
def test_default_profile_empty_snap_multiple_heals_to_8():
# "" (the exact reported failure value) must not break the resolver: default snaps at mult 8.
# 1005/8 = 125.625 -> 126 -> 1008 (no .5 boundary, so Python round == JS Math.round).
assert resolve_dims("default", "raw", SQ, "landscape", "", 1.0, 1005, 1005) == (1008, 1008)
def test_ideogram_ignores_empty_snap_multiple():
# The core invariant: Ideogram always snaps to 16 regardless of a garbage snap_multiple.
assert resolve_dims("Ideogram 4", "raw", SQ, "landscape", "", 1.0, 1050, 1050) == (1056, 1056)
def test_ideogram_output_invariant_across_bad_snap_multiple():
# Property: for ANY snap_multiple, Ideogram yields identical dims, all multiples of 16 in [256,2048].
ref = resolve_dims("Ideogram 4", "megapixel", WS, "landscape", 8, 4.0, 0, 0)
for snap in ("", 0, -5, None, 1, 64, 3000):
w, h = resolve_dims("Ideogram 4", "megapixel", WS, "landscape", snap, 4.0, 0, 0)
assert (w, h) == ref
assert w % 16 == 0 and h % 16 == 0
assert 256 <= w <= 2048 and 256 <= h <= 2048