200 lines
7.3 KiB
Python
200 lines
7.3 KiB
Python
import math
|
|
import pytest
|
|
from random_resolution_picker.random_resolution_picker import (
|
|
_parse_resolutions,
|
|
RandomResolutionPicker,
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# _parse_resolutions — valid input
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_parse_basic():
|
|
assert _parse_resolutions("1328,1328\n1664,928") == [(1328, 1328), (1664, 928)]
|
|
|
|
|
|
def test_parse_skips_blank_lines():
|
|
assert _parse_resolutions("\n1328,1328\n\n928,1664\n") == [(1328, 1328), (928, 1664)]
|
|
|
|
|
|
def test_parse_skips_comment_lines():
|
|
assert _parse_resolutions("# square\n1328,1328") == [(1328, 1328)]
|
|
|
|
|
|
def test_parse_strips_surrounding_whitespace():
|
|
assert _parse_resolutions(" 1328 , 1328 ") == [(1328, 1328)]
|
|
|
|
|
|
def test_parse_duplicate_lines_kept_as_weighted_entries():
|
|
# Duplicates are intentional — they increase selection probability.
|
|
result = _parse_resolutions("512,512\n512,512\n1024,1024")
|
|
assert result == [(512, 512), (512, 512), (1024, 1024)]
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# _parse_resolutions — invalid input
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_parse_raises_missing_comma_includes_line_number():
|
|
with pytest.raises(ValueError, match=r"Line 2:.*exactly one comma"):
|
|
_parse_resolutions("# ok\n1328")
|
|
|
|
|
|
def test_parse_raises_too_many_commas_includes_line_number():
|
|
with pytest.raises(ValueError, match=r"Line 1:.*exactly one comma"):
|
|
_parse_resolutions("1328,928,64")
|
|
|
|
|
|
def test_parse_raises_non_integer_width_includes_line_number():
|
|
with pytest.raises(ValueError, match=r"Line 3:.*integers"):
|
|
_parse_resolutions("512,512\n1024,1024\nabc,512")
|
|
|
|
|
|
def test_parse_raises_non_integer_height_includes_line_number():
|
|
with pytest.raises(ValueError, match=r"Line 1:.*integers"):
|
|
_parse_resolutions("512,abc")
|
|
|
|
|
|
def test_parse_raises_zero_width_includes_line_number():
|
|
with pytest.raises(ValueError, match=r"Line 2:.*positive"):
|
|
_parse_resolutions("512,512\n0,1328")
|
|
|
|
|
|
def test_parse_raises_zero_height_includes_line_number():
|
|
with pytest.raises(ValueError, match=r"Line 1:.*positive"):
|
|
_parse_resolutions("512,0")
|
|
|
|
|
|
def test_parse_raises_negative_value_includes_line_number():
|
|
with pytest.raises(ValueError, match=r"Line 1:.*positive"):
|
|
_parse_resolutions("-512,1024")
|
|
|
|
|
|
def test_parse_raises_empty_list_all_comments():
|
|
with pytest.raises(ValueError, match="empty"):
|
|
_parse_resolutions("# comment only\n\n")
|
|
|
|
|
|
def test_parse_raises_empty_string():
|
|
with pytest.raises(ValueError, match="empty"):
|
|
_parse_resolutions("")
|
|
|
|
|
|
def test_parse_raises_whitespace_only():
|
|
with pytest.raises(ValueError, match="empty"):
|
|
_parse_resolutions(" \n\t\n ")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# RandomResolutionPicker.pick — determinism and output shape
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_pick_same_seed_returns_same_result():
|
|
node = RandomResolutionPicker()
|
|
r1 = node.pick("1328,1328\n1664,928", auto_randomize=False, seed=42)
|
|
r2 = node.pick("1328,1328\n1664,928", auto_randomize=False, seed=42)
|
|
assert r1 == r2
|
|
|
|
|
|
def test_pick_different_seeds_can_return_different_results():
|
|
node = RandomResolutionPicker()
|
|
# With a two-entry list, seeds 0 and 1 should diverge eventually.
|
|
results = {
|
|
node.pick("1328,1328\n1664,928", auto_randomize=False, seed=s)[:2]
|
|
for s in range(20)
|
|
}
|
|
assert len(results) > 1, "20 different seeds all produced the same pair — suspicious"
|
|
|
|
|
|
def test_pick_result_is_always_from_the_list():
|
|
node = RandomResolutionPicker()
|
|
valid = {(1328, 1328), (1664, 928), (928, 1664)}
|
|
for seed in range(50):
|
|
w, h, label = node.pick("1328,1328\n1664,928\n928,1664", auto_randomize=False, seed=seed)
|
|
assert (w, h) in valid
|
|
assert label == f"{w}x{h}"
|
|
|
|
|
|
def test_pick_single_entry_always_returns_it():
|
|
node = RandomResolutionPicker()
|
|
for seed in range(10):
|
|
w, h, _ = node.pick("512,768", auto_randomize=False, seed=seed)
|
|
assert (w, h) == (512, 768)
|
|
|
|
|
|
def test_pick_label_format():
|
|
node = RandomResolutionPicker()
|
|
w, h, label = node.pick("1024,768", auto_randomize=False, seed=0)
|
|
assert label == "1024x768"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# IS_CHANGED — cache invalidation contract
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_is_changed_returns_nan_when_auto_randomize():
|
|
result = RandomResolutionPicker.IS_CHANGED("1328,1328", auto_randomize=True, seed=0)
|
|
assert math.isnan(result), "auto_randomize=True must return NaN to force re-execution"
|
|
|
|
|
|
def test_is_changed_returns_equal_value_for_same_inputs():
|
|
r1 = RandomResolutionPicker.IS_CHANGED("1328,1328", auto_randomize=False, seed=5)
|
|
r2 = RandomResolutionPicker.IS_CHANGED("1328,1328", auto_randomize=False, seed=5)
|
|
assert r1 == r2
|
|
|
|
|
|
def test_is_changed_differs_when_seed_changes():
|
|
r1 = RandomResolutionPicker.IS_CHANGED("1328,1328", auto_randomize=False, seed=5)
|
|
r2 = RandomResolutionPicker.IS_CHANGED("1328,1328", auto_randomize=False, seed=6)
|
|
assert r1 != r2
|
|
|
|
|
|
def test_is_changed_differs_when_resolutions_text_changes():
|
|
r1 = RandomResolutionPicker.IS_CHANGED("1328,1328", auto_randomize=False, seed=5)
|
|
r2 = RandomResolutionPicker.IS_CHANGED("1664,928", auto_randomize=False, seed=5)
|
|
assert r1 != r2
|
|
|
|
|
|
def test_is_changed_auto_randomize_false_is_not_nan():
|
|
result = RandomResolutionPicker.IS_CHANGED("1328,1328", auto_randomize=False, seed=0)
|
|
assert result == ("1328,1328", 0)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# extra_pnginfo — metadata embedding
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_pick_writes_metadata_to_extra_pnginfo():
|
|
node = RandomResolutionPicker()
|
|
meta = {}
|
|
w, h, label = node.pick("512,768", auto_randomize=False, seed=0, extra_pnginfo=meta)
|
|
assert "RandomResolutionPicker" in meta
|
|
data = meta["RandomResolutionPicker"]
|
|
assert data["resolution"] == label
|
|
assert data["resolution_width"] == w
|
|
assert data["resolution_height"] == h
|
|
assert data["resolution_seed"] == 0
|
|
|
|
|
|
def test_pick_extra_pnginfo_none_does_not_raise():
|
|
node = RandomResolutionPicker()
|
|
# Must not raise even when ComfyUI passes None (no Save Image node downstream).
|
|
node.pick("512,768", auto_randomize=False, seed=0, extra_pnginfo=None)
|
|
|
|
|
|
def test_pick_extra_pnginfo_non_dict_does_not_raise():
|
|
node = RandomResolutionPicker()
|
|
# ComfyUI may pass unexpected types; guard must be isinstance check, not truthiness.
|
|
node.pick("512,768", auto_randomize=False, seed=0, extra_pnginfo="unexpected")
|
|
|
|
|
|
def test_pick_auto_randomize_true_picks_from_list_and_records_seed():
|
|
node = RandomResolutionPicker()
|
|
from random_resolution_picker.random_resolution_picker import MAX_SEED
|
|
meta = {}
|
|
w, h, label = node.pick("512,768\n1024,1024", auto_randomize=True, seed=0, extra_pnginfo=meta)
|
|
assert (w, h) in {(512, 768), (1024, 1024)}
|
|
assert label == f"{w}x{h}"
|
|
assert 0 <= meta["RandomResolutionPicker"]["resolution_seed"] <= MAX_SEED
|