Files
ausboss-ComfyUI-AusBoss/tests/test_memory_helpers.py
T
ausboss 3f715a4af8 Ship the crush-list build-out: 7 new node files, replace button, video and LoRA polish
- New nodes: Image Resize plus the Utility group (Text, Integer, Float,
  Show Text, Math Expression, Select Every Nth, Split Batch, Merge
  Batches, Free Memory) - the dependency-free gaps that let shared
  workflows drop heavy packs.
- Replace with AusBoss nodes (prototype): preview-then-confirm graph
  rewrite swapping known third-party nodes for pack equivalents, with a
  tested translation map, per-node opt-out, and refuse-with-reason for
  nodes the pack cannot stand in for.
- Load Video: single-frame FRAME picker, an honest "what will load"
  label from the probed source rate, drop-a-video-to-restore. Save
  Video: format-aware widgets and a file_path output. Stitch Inpaint
  gained a blend_mask output, Select Frame negative from-the-end
  indexing, Image Size a batch count - all appended, saved workflows
  load unchanged.
- LoRA Loader: drag-to-reorder rows, hover thumbnails on rows, file
  size and date in the info card.
- Appearance: flagship scheme deepened to slate-teal; the retired
  bright-teal pair upgrades automatically via LEGACY_SCHEME_PAIRS.
  Optional queue-completion chime, off by default.
- Image Resize invariant: pixels are only invented on an explicit
  width+height pad request - scale-derived modes resolve divisible_by
  snaps with a sub-half-step resize, never bars.
- Docs refreshed for six stale pages, stale text and metadata fixed
  across the pack, the panel-height rule codified in AGENTS.md and the
  brand skill, and ausboss_node_tour.json added - a model-free example
  exercising 17 node types in one graph.

Validated: scripts/validate_nodes.py (24 files), 300 JS tests, all
Python suites under the ComfyUI embedded interpreter.
2026-08-27 13:30:35 -05:00

91 lines
3.3 KiB
Python

from __future__ import annotations
from pathlib import Path
import sys
import types
import unittest
from unittest.mock import patch
ROOT = Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT))
if "nodes" in sys.modules and not hasattr(sys.modules["nodes"], "__path__"):
del sys.modules["nodes"]
from nodes._memory_helpers import WILDCARD, free_gpu_memory
class WildcardTypeTests(unittest.TestCase):
def test_it_is_the_star_string(self):
self.assertEqual(str(WILDCARD), "*")
self.assertIsInstance(WILDCARD, str)
def test_it_is_never_unequal_in_either_direction(self):
# ComfyUI validates links with !=, and the upstream type can sit on
# either side of the comparison. Both directions run our __ne__
# because a str subclass's reflected comparison takes priority.
self.assertFalse(WILDCARD != "IMAGE")
self.assertFalse("IMAGE" != WILDCARD)
self.assertFalse(WILDCARD != "LATENT")
self.assertFalse(WILDCARD != WILDCARD)
def test_plain_equality_and_hashing_still_behave_as_str(self):
self.assertTrue(WILDCARD == "*")
self.assertEqual(hash(WILDCARD), hash("*"))
def fake_model_management(**functions):
"""A stand-in comfy.model_management installed into sys.modules."""
comfy = types.ModuleType("comfy")
module = types.ModuleType("comfy.model_management")
for name, function in functions.items():
setattr(module, name, function)
comfy.model_management = module
return {"comfy": comfy, "comfy.model_management": module}
class FreeGpuMemoryTests(unittest.TestCase):
def test_it_never_raises_and_reports_what_it_did(self):
# This process has no comfy on the path; the comfy steps are skipped
# and the always-available ones still run.
freed = free_gpu_memory()
self.assertIsInstance(freed, list)
self.assertIn("collected garbage", freed)
self.assertTrue(all(isinstance(entry, str) for entry in freed))
def test_comfy_steps_are_reported_when_comfy_is_present(self):
calls = []
modules = fake_model_management(
unload_all_models=lambda: calls.append("unload"),
soft_empty_cache=lambda: calls.append("soft_empty"),
)
with patch.dict(sys.modules, modules):
freed = free_gpu_memory()
self.assertEqual(calls, ["unload", "soft_empty"])
self.assertIn("unloaded models", freed)
self.assertIn("emptied comfy cache", freed)
def test_a_moved_api_is_skipped_not_fatal(self):
def broken():
raise RuntimeError("this API moved in a core release")
modules = fake_model_management(
unload_all_models=broken,
soft_empty_cache=lambda: None,
)
with patch.dict(sys.modules, modules):
freed = free_gpu_memory()
self.assertNotIn("unloaded models", freed)
self.assertIn("emptied comfy cache", freed)
def test_a_missing_api_is_skipped_not_fatal(self):
modules = fake_model_management() # neither function exists
with patch.dict(sys.modules, modules):
freed = free_gpu_memory()
self.assertNotIn("unloaded models", freed)
self.assertNotIn("emptied comfy cache", freed)
self.assertIn("collected garbage", freed)
if __name__ == "__main__":
unittest.main()