Files
ausboss-ComfyUI-AusBoss/tests/test_compare_node.py
T
ausboss d41aeaa1d6 Image Compare A/B: a sliding mode switch, a named save, labels that scrub
The four mode buttons become one switch, Slide | A | B | Erase, whose
marker slides to the choice. In Erase the brush's two jobs and the
picture on top are switches of the same kind, and PSD is a plain
off | on.

Save moves to the bottom right of the node, larger, with a disk icon,
in a row of its own with the name of the files and the PSD choice.

The files can be named. A Name field on the node takes letters,
digits, spaces, dots, dashes and underscores; the server checks the
same rule and refuses anything else, so a folder is never part of a
name. A new optional filename input takes a wired name, such as the
source picture's file name: its folder and extension are dropped. The
save route still names no file to read.

Size and Soft scrub from their label and from the unit inside the box
as well as from the number (the scrub control gains handle() for this).

Workflows saved before load as they were: 60 that use the node were
opened (the pack's examples, published workflows, the oldest saved
shapes), 72 nodes kept their size, mode and links. The node is one
input socket taller.
2026-10-13 15:16:57 -05:00

100 lines
4.3 KiB
Python

from __future__ import annotations
import sys
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
import torch
from PIL import Image
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 import _preview_helpers, node_compare
from nodes._compare_helpers import pair_pictures
from nodes.node_compare import AusBossCompare, first_frame_to_pil
def fake_folder_paths(temp_dir: str):
class FakeFolderPaths:
@staticmethod
def get_temp_directory():
return temp_dir
@staticmethod
def get_save_image_path(prefix, output_dir, _width, _height):
folder = Path(output_dir) / "ausboss"
folder.mkdir(parents=True, exist_ok=True)
return str(folder), prefix, 1, "ausboss", prefix
return FakeFolderPaths
class CompareNodeTests(unittest.TestCase):
def test_first_frame_conversion_and_validation(self):
batch = torch.zeros(3, 4, 6, 3)
batch[0, :, :, 0] = 1.0 # first frame is pure red
preview = first_frame_to_pil(batch)
self.assertEqual((preview.width, preview.height), (6, 4))
self.assertEqual(preview.getpixel((0, 0)), (255, 0, 0))
with self.assertRaisesRegex(ValueError, "BHWC"):
first_frame_to_pil(torch.zeros(4, 6, 3))
with self.assertRaisesRegex(ValueError, "empty"):
first_frame_to_pil(torch.zeros(0, 4, 6, 3))
def test_compare_saves_temp_previews_and_passes_a_through(self):
image_a = torch.rand(2, 8, 12, 3)
image_b = torch.rand(1, 8, 12, 3)
with tempfile.TemporaryDirectory() as tmp:
# folder_paths is imported by the shared preview helper now, which
# is where every node's temp preview is written.
with patch.object(_preview_helpers, "folder_paths", fake_folder_paths(tmp)), \
patch.object(node_compare, "folder_paths", fake_folder_paths(tmp)):
result = AusBossCompare().compare(image_a, image_b)
self.assertIs(result["result"][0], image_a)
for key in ("a_images", "b_images"):
(ref,) = result["ui"][key]
self.assertEqual(ref["type"], "temp")
self.assertEqual(ref["subfolder"], "ausboss")
self.assertEqual((ref["width"], ref["height"]), (12, 8))
saved = Path(tmp) / "ausboss" / ref["filename"]
with Image.open(saved) as png:
self.assertEqual(png.size, (12, 8))
# The run's key stands for exactly these two files; Erase's Save
# sends the key back and never a file name.
(key,) = result["ui"]["pair"]
self.assertEqual(
pair_pictures(key)[:2],
tuple(Path(tmp) / "ausboss" / result["ui"][name][0]["filename"] for name in ("a_images", "b_images")),
)
# No name wired in: the one typed on the node will be used.
self.assertEqual((result["ui"]["save_name"], pair_pictures(key)[2]), ([""], ""))
def test_a_wired_filename_names_the_saved_files(self):
image = torch.rand(1, 8, 12, 3)
with tempfile.TemporaryDirectory() as tmp:
with patch.object(_preview_helpers, "folder_paths", fake_folder_paths(tmp)), \
patch.object(node_compare, "folder_paths", fake_folder_paths(tmp)):
result = AusBossCompare().compare(image, image, filename="sets/a/photo 12.jpg")
self.assertEqual(result["ui"]["save_name"], ["photo 12"])
self.assertEqual(pair_pictures(result["ui"]["pair"][0])[2], "photo 12")
self.assertIs(result["result"][0], image)
optional = AusBossCompare.INPUT_TYPES()["optional"]["filename"]
self.assertEqual(optional[0], "STRING")
self.assertTrue(optional[1]["forceInput"])
self.assertEqual(list(AusBossCompare.INPUT_TYPES()["required"]), ["image_a", "image_b"])
def test_two_nodes_use_distinct_temp_prefixes(self):
# Prefixes are randomized per instance so parallel Compare nodes
# never overwrite each other's previews.
self.assertNotEqual(AusBossCompare()._prefix, AusBossCompare()._prefix)
if __name__ == "__main__":
unittest.main()