- random_seed_generator.py: only update the shared _last_seed counter for random/increment/decrement modes. fixed mode must be side-effect-free so interleaved fixed reads do not clobber the counter used by other AdvancedSeedGenerator nodes in the same workflow. - tests: add regression coverage for the new gating (fixed leaves _last_seed alone; random and increment write through). Rewrite reset_state test to set state directly instead of relying on the removed fixed side effect. - pyproject.toml: rewrite description to drop the stale "cross-library synchronization" claim (feature removed in 2.3.0). - README: replace the unrealistic "ComfyUI Python console" reset advice with three actionable options (restart, random-then-increment, and reset_state for scripts).
148 lines
4.6 KiB
Python
148 lines
4.6 KiB
Python
"""Pytest suite for AdvancedSeedGenerator.
|
|
|
|
Covers the four modes (fixed, random, increment, decrement), wrap behavior at
|
|
both 64-bit boundaries, IS_CHANGED contract, and reset_state.
|
|
"""
|
|
|
|
import sys
|
|
import time
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
# Allow `import random_seed_generator` when pytest is run from the repo root.
|
|
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
|
|
|
from random_seed_generator import ( # noqa: E402
|
|
MAX_SEED,
|
|
MIN_SEED,
|
|
NODE_CLASS_MAPPINGS,
|
|
NODE_DISPLAY_NAME_MAPPINGS,
|
|
AdvancedSeedGenerator,
|
|
)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _reset_class_state():
|
|
"""`_last_seed` is class-level shared state — reset before every test."""
|
|
AdvancedSeedGenerator.reset_state()
|
|
yield
|
|
AdvancedSeedGenerator.reset_state()
|
|
|
|
|
|
@pytest.fixture
|
|
def gen():
|
|
return AdvancedSeedGenerator()
|
|
|
|
|
|
def test_fixed_returns_input_seed(gen):
|
|
assert gen.generate_seed("fixed", 42) == (42,)
|
|
|
|
|
|
def test_fixed_does_not_mutate_last_seed(gen):
|
|
"""Regression: fixed mode must not touch the shared increment/decrement counter.
|
|
|
|
Before this guard, calling `fixed` clobbered `_last_seed`, which silently
|
|
altered other AdvancedSeedGenerator nodes running increment/decrement in the
|
|
same workflow.
|
|
"""
|
|
AdvancedSeedGenerator._last_seed = 500
|
|
assert gen.generate_seed("fixed", 12345) == (12345,)
|
|
assert AdvancedSeedGenerator._last_seed == 500
|
|
|
|
|
|
def test_random_updates_last_seed(gen):
|
|
AdvancedSeedGenerator._last_seed = 0
|
|
(result,) = gen.generate_seed("random", 0)
|
|
assert AdvancedSeedGenerator._last_seed == result
|
|
|
|
|
|
def test_increment_updates_last_seed(gen):
|
|
AdvancedSeedGenerator._last_seed = 100
|
|
gen.generate_seed("increment", 0)
|
|
assert AdvancedSeedGenerator._last_seed == 101
|
|
|
|
|
|
def test_random_within_bounds(gen):
|
|
(result,) = gen.generate_seed("random", 0)
|
|
assert isinstance(result, int)
|
|
assert MIN_SEED <= result <= MAX_SEED
|
|
|
|
|
|
def test_random_produces_different_values(gen):
|
|
samples = {gen.generate_seed("random", 0)[0] for _ in range(20)}
|
|
# 20 draws from a 2^64 space colliding 19 times would be a miracle.
|
|
assert len(samples) > 1
|
|
|
|
|
|
def test_increment_advances_last_seed(gen):
|
|
AdvancedSeedGenerator._last_seed = 100
|
|
assert gen.generate_seed("increment", 0) == (101,)
|
|
assert gen.generate_seed("increment", 0) == (102,)
|
|
|
|
|
|
def test_decrement_decreases_last_seed(gen):
|
|
AdvancedSeedGenerator._last_seed = 100
|
|
assert gen.generate_seed("decrement", 0) == (99,)
|
|
assert gen.generate_seed("decrement", 0) == (98,)
|
|
|
|
|
|
def test_increment_wraps_at_max(gen):
|
|
AdvancedSeedGenerator._last_seed = MAX_SEED
|
|
assert gen.generate_seed("increment", 0) == (MIN_SEED,)
|
|
|
|
|
|
def test_decrement_wraps_at_min(gen):
|
|
AdvancedSeedGenerator._last_seed = MIN_SEED
|
|
assert gen.generate_seed("decrement", 0) == (MAX_SEED,)
|
|
|
|
|
|
def test_unknown_mode_raises_value_error(gen):
|
|
with pytest.raises(ValueError, match="Unknown mode"):
|
|
gen.generate_seed("teleport", 0)
|
|
|
|
|
|
def test_is_changed_dynamic_modes_return_timestamp():
|
|
for mode in ("random", "increment", "decrement"):
|
|
before = time.time()
|
|
result = AdvancedSeedGenerator.IS_CHANGED(mode, 0)
|
|
after = time.time()
|
|
assert isinstance(result, float)
|
|
assert before <= result <= after
|
|
|
|
|
|
def test_is_changed_fixed_returns_stable_key():
|
|
assert AdvancedSeedGenerator.IS_CHANGED("fixed", 99) == "fixed_99"
|
|
assert AdvancedSeedGenerator.IS_CHANGED("fixed", 99) == "fixed_99"
|
|
|
|
|
|
def test_reset_state_clears_last_seed(gen):
|
|
AdvancedSeedGenerator._last_seed = 7777
|
|
assert AdvancedSeedGenerator._last_seed == 7777
|
|
AdvancedSeedGenerator.reset_state()
|
|
assert AdvancedSeedGenerator._last_seed == 0
|
|
|
|
|
|
def test_node_registration_mappings():
|
|
assert NODE_CLASS_MAPPINGS == {"AdvancedSeedGenerator": AdvancedSeedGenerator}
|
|
assert "AdvancedSeedGenerator" in NODE_DISPLAY_NAME_MAPPINGS
|
|
|
|
|
|
def test_input_types_schema_shape():
|
|
schema = AdvancedSeedGenerator.INPUT_TYPES()
|
|
assert "required" in schema
|
|
assert set(schema["required"].keys()) == {"mode", "seed"}
|
|
mode_choices, *_ = schema["required"]["mode"]
|
|
assert set(mode_choices) == {"fixed", "random", "increment", "decrement"}
|
|
seed_type, seed_meta = schema["required"]["seed"]
|
|
assert seed_type == "INT"
|
|
assert seed_meta["min"] == MIN_SEED
|
|
assert seed_meta["max"] == MAX_SEED
|
|
|
|
|
|
def test_comfyui_class_attributes():
|
|
assert AdvancedSeedGenerator.RETURN_TYPES == ("INT",)
|
|
assert AdvancedSeedGenerator.RETURN_NAMES == ("seed",)
|
|
assert AdvancedSeedGenerator.FUNCTION == "generate_seed"
|
|
assert AdvancedSeedGenerator.CATEGORY == "utils"
|