diff --git a/tests/test_manager_scan.py b/tests/test_manager_scan.py new file mode 100644 index 0000000..63f4289 --- /dev/null +++ b/tests/test_manager_scan.py @@ -0,0 +1,37 @@ +"""ComfyUI-Manager reads every .py file of a plugin, tests too, and lists whatever looks like a node +table as that plugin's nodes. AMB has none (AD-024), so no file may look like one: a table in a test once +made Manager offer AMB to people who missed Power Lora Loader, in place of rgthree.""" +import re +import unittest +from pathlib import Path + +PLUGIN = Path(__file__).resolve().parents[1] + +# As ComfyUI-Manager's scanner.py (scan_in_file) has them. +TABLE = re.compile(r"_CLASS_MAPPINGS\s*(?::\s*\w+\s*)?=\s*(?:\\\s*)?{([^}]*)}", re.MULTILINE | re.DOTALL) +OTHERS = (r'^[^=]*_CLASS_MAPPINGS\["(.*?)"\]', r"^[^=]*_CLASS_MAPPINGS\['(.*?)'\]", + r'@register_node\("(.+)",\s*\".+"\)', r'"(\w+)"\s*:\s*{"class":\s*\w+\s*') + + +def claims(code): + """What the scanner would take for nodes in `code`: the tables with something in them, and the other forms.""" + found = [table.strip() for table in TABLE.findall(code) if table.strip()] + return found + [name for pattern in OTHERS for name in re.findall(pattern, code)] + + +class ManagerScanTests(unittest.TestCase): + def test_the_check_sees_a_table(self): + name = "NODE_CLASS_" + "MAPPINGS" # in two, or this file would be the one to look like a table + self.assertTrue(claims(name + ' = {"Some Node": object}')) + self.assertFalse(claims(name + " = {}")) + + def test_no_file_looks_like_a_node_table(self): + for path in PLUGIN.rglob("*.py"): + parts = path.relative_to(PLUGIN).parts + if "__pycache__" in parts or any(part.startswith(".") for part in parts): + continue # the scanner leaves these out too + self.assertEqual(claims(path.read_text(encoding="utf-8", errors="ignore")), [], path) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_model_usage.py b/tests/test_model_usage.py index e2c3550..a9c0325 100644 --- a/tests/test_model_usage.py +++ b/tests/test_model_usage.py @@ -65,18 +65,19 @@ class UsageTests(unittest.TestCase): {"type": "loras", "root": 0, "rel": "never.safetensors", "size": 9}, {"type": "vae", "root": 0, "rel": "hashed.safetensors", "size": 1}, ] + registered = { # by this name, not as the keyword's value: tests/test_manager_scan.py says why + "KSampler": node("nodes", ["MODEL"], ["LATENT"]), + "Power Lora Loader (rgthree)": node("custom_nodes.rgthree-comfy", ["MODEL"], ["MODEL"]), + "easy loraStack": node("custom_nodes.ComfyUI-Easy-Use"), + "easy seed": node("custom_nodes.ComfyUI-Easy-Use.py.nodes"), + "Unused Thing": node("custom_nodes.forgotten-pack"), + "DetailerForEach": node("custom_nodes.ComfyUI-Impact-Pack", ["IMAGE", "BBOX_DETECTOR"], ["IMAGE"]), + "UltralyticsDetectorProvider": node("custom_nodes.comfyui-impact-subpack", [], ["BBOX_DETECTOR"]), + "Genie Speak": node("custom_nodes.Genie-TTS", ["STRING"], ["AUDIO"]), + "Manager Thing": node("custom_nodes.ComfyUI-Manager"), + } nodes = types.SimpleNamespace( - NODE_CLASS_MAPPINGS={ - "KSampler": node("nodes", ["MODEL"], ["LATENT"]), - "Power Lora Loader (rgthree)": node("custom_nodes.rgthree-comfy", ["MODEL"], ["MODEL"]), - "easy loraStack": node("custom_nodes.ComfyUI-Easy-Use"), - "easy seed": node("custom_nodes.ComfyUI-Easy-Use.py.nodes"), - "Unused Thing": node("custom_nodes.forgotten-pack"), - "DetailerForEach": node("custom_nodes.ComfyUI-Impact-Pack", ["IMAGE", "BBOX_DETECTOR"], ["IMAGE"]), - "UltralyticsDetectorProvider": node("custom_nodes.comfyui-impact-subpack", [], ["BBOX_DETECTOR"]), - "Genie Speak": node("custom_nodes.Genie-TTS", ["STRING"], ["AUDIO"]), - "Manager Thing": node("custom_nodes.ComfyUI-Manager"), - }, + NODE_CLASS_MAPPINGS=registered, LOADED_MODULE_DIRS={name: str(custom / name) for name in packs}, EXTENSION_WEB_DIRS={"rgthree-comfy": str(custom / "rgthree-comfy" / "js"), "page-only": str(custom / "page-only" / "web")}, ) diff --git a/tests/test_tts_version.py b/tests/test_tts_version.py index 1e8c597..0758b9e 100644 --- a/tests/test_tts_version.py +++ b/tests/test_tts_version.py @@ -11,7 +11,8 @@ PLUGIN = Path(__file__).resolve().parents[1] sys.path[:0] = [str(PLUGIN), str(PLUGIN.parents[1])] from api import tts_version, version_manager as vm -NODES = 'NODE_CLASS_MAPPINGS = {"AnomalousTTS_CharacterSpeech": object}\n' +MAPPINGS = 'NODE_CLASS_' + 'MAPPINGS' # in two: tests/test_manager_scan.py says why +NODES = MAPPINGS + ' = {"AnomalousTTS_CharacterSpeech": object}\n' def git(cwd, *args): @@ -51,7 +52,7 @@ class TtsVersionTests(unittest.TestCase): def test_only_the_official_clone_is_found(self): self.assertIsNone(tts_version.find_checkout()) self.clone('A_fork', 'https://github.com/someone/Anomalous_TTS.git') - self.clone('Other_pack', 'https://github.com/DemonGatanjieu/Anomalous_TTS', nodes='NODE_CLASS_MAPPINGS = {}\n') + self.clone('Other_pack', 'https://github.com/DemonGatanjieu/Anomalous_TTS', nodes=MAPPINGS + ' = {}\n') (self.nodes / 'Plain_copy').mkdir() Path(self.nodes / 'Plain_copy' / 'nodes.py').write_text(NODES, encoding='utf-8') # no git: Manager's zip, a copy self.assertIsNone(tts_version.find_checkout())