Files
ComfyUI/tests-unit/comfy_api_test/test_weight_cache.py
benjcooley f23c8a57c2 refactor: extract bounded weight cache and fix LRU eviction
Ports 31d76988 from api-v2-runtime so both cores carry the same cache
behaviour. Seven of the thirteen copy-pasted caches evicted with
dict.popitem(), dropping the most recently inserted entry rather than the
least recently used, which pinned whichever model loaded first.
2026-09-01 16:01:57 -07:00

108 lines
3.2 KiB
Python

import os
import pytest
from comfy_api.latest._weight_cache import WeightCache, weight_identity
def _write(tmp_path, name, body="w"):
path = tmp_path / name
path.write_text(body)
return str(path)
def test_hit_reuses_entry_without_reloading(tmp_path):
path = _write(tmp_path, "a.bin")
cache = WeightCache(load=lambda p: object(), max_entries=2)
first = cache.get(path)
second = cache.get(path)
assert first is second
assert (cache.loads, cache.hits) == (1, 1)
def test_evicts_least_recently_used_not_most_recent(tmp_path):
"""A cache that evicts the newest entry pins whatever loaded first.
The copy-pasted caches this replaces used ``dict.popitem()``, which drops
the most recently inserted entry, so a workflow alternating between two
models reloaded both every time while the first one it ever saw was never
reclaimed.
"""
a, b, c = (_write(tmp_path, n) for n in ("a.bin", "b.bin", "c.bin"))
cache = WeightCache(load=lambda p: p, max_entries=2)
cache.get(a)
cache.get(b)
cache.get(a) # a is now the most recently used
cache.get(c) # must evict b, the least recently used
loads_before = cache.loads
cache.get(a)
assert cache.loads == loads_before, "a was evicted despite recent use"
cache.get(b)
assert cache.loads == loads_before + 1, "b should have been the eviction"
def test_release_runs_on_eviction(tmp_path):
a, b = (_write(tmp_path, n) for n in ("a.bin", "b.bin"))
released = []
cache = WeightCache(
load=lambda p: p, max_entries=1, release=released.append)
cache.get(a)
cache.get(b)
assert released == [a]
def test_discriminators_separate_entries_from_one_file(tmp_path):
path = _write(tmp_path, "a.bin")
cache = WeightCache(load=lambda p, variant: variant, max_entries=4)
assert cache.get(path, "base") == "base"
assert cache.get(path, "large") == "large"
assert cache.loads == 2
def test_rewritten_file_is_not_served_from_cache(tmp_path, monkeypatch):
path = _write(tmp_path, "a.bin", "one")
cache = WeightCache(load=lambda p: open(p).read(), max_entries=2)
assert cache.get(path) == "one"
# Same path, new contents: identity must change so the stale entry is not
# returned. mtime_ns can collide on a coarse clock, so force it forward.
with open(path, "w") as handle:
handle.write("two")
stat = os.stat(path)
os.utime(path, ns=(stat.st_atime_ns, stat.st_mtime_ns + 1_000_000))
assert cache.get(path) == "two"
def test_identity_changes_when_contents_change(tmp_path):
path = _write(tmp_path, "a.bin", "one")
before = weight_identity(path)
with open(path, "w") as handle:
handle.write("a much longer body")
assert weight_identity(path) != before
def test_max_entries_must_be_positive():
with pytest.raises(ValueError):
WeightCache(load=lambda p: p, max_entries=0)
def test_clear_releases_all_and_reports_count(tmp_path):
a, b = (_write(tmp_path, n) for n in ("a.bin", "b.bin"))
released = []
cache = WeightCache(
load=lambda p: p, max_entries=4, release=released.append)
cache.get(a)
cache.get(b)
assert cache.clear() == 2
assert sorted(released) == sorted([a, b])
assert cache.clear() == 0