113 lines
4.6 KiB
Python
113 lines
4.6 KiB
Python
import importlib
|
|
import inspect
|
|
import sys
|
|
import types
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
from comfy_api.latest import io
|
|
|
|
|
|
RECEIVERS = [
|
|
("merger", 10), ("lora_extract_svd", 5), ("dora_extract_wd", 5),
|
|
("dora_learned_wd", 5), ("text_encoder_extract", 10), ("lora_resize", 4),
|
|
]
|
|
|
|
|
|
def load(name):
|
|
package_name = "modelutils_layer_receiver_tests"
|
|
if package_name not in sys.modules:
|
|
package = types.ModuleType(package_name)
|
|
package.__path__ = [str(Path(__file__).resolve().parents[1])]
|
|
sys.modules[package_name] = package
|
|
return importlib.import_module(f"{package_name}.nodes.{name}")
|
|
|
|
|
|
def nodes(module):
|
|
return [
|
|
node for name, node in vars(module).items()
|
|
if isinstance(node, type) and not name.startswith("_")
|
|
and node.__module__ == module.__name__ and issubclass(node, io.ComfyNode)
|
|
and (not module.__name__.endswith(".lora_resize") or name.startswith("LoRAResize"))
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize("module_name,count", RECEIVERS)
|
|
def test_all_39_receivers_append_optional_typed_socket(module_name, count):
|
|
module = load(module_name)
|
|
classes = nodes(module)
|
|
assert len(classes) == count
|
|
config_output = load("layer_parameters").LayerParameterConfiguration.define_schema().outputs[0]
|
|
for node in classes:
|
|
schema = node.define_schema()
|
|
input_ids = [item.id for item in schema.inputs]
|
|
assert input_ids[-2:] == ["include_mode", "layer_parameters"], schema.node_id
|
|
assert input_ids.count("layer_parameters") == 1
|
|
socket = schema.inputs[-1]
|
|
assert socket.optional
|
|
assert socket.Parent.io_type == config_output.Parent.io_type == "MODELUTILS_LAYER_PARAMETERS"
|
|
assert "a=" in socket.tooltip
|
|
assert inspect.signature(node.execute).parameters["layer_parameters"].default is None
|
|
|
|
|
|
@pytest.mark.parametrize("module_name,count", RECEIVERS)
|
|
def test_all_39_receivers_forward_configuration(monkeypatch, module_name, count):
|
|
module = load(module_name)
|
|
rules = load("layer_parameters").parse_rules("(layer) a:2")
|
|
captured = []
|
|
helper_names = {
|
|
"lora_extract_svd": "extract_lora_from_files",
|
|
"dora_extract_wd": "extract_dora_from_files",
|
|
"dora_learned_wd": "extract_dora_learned_from_files",
|
|
"text_encoder_extract": "extract_te_from_files",
|
|
"lora_resize": "resize_lora_file",
|
|
}
|
|
if module_name == "merger":
|
|
def merge(model_names, calc_mode, all_modes, params, model_type):
|
|
captured.append(params["layer_parameters"])
|
|
return "result.safetensors"
|
|
monkeypatch.setattr(module.MergerLogic, "execute_merge", merge)
|
|
else:
|
|
name = helper_names[module_name]
|
|
signature = inspect.signature(getattr(module, name))
|
|
|
|
def process(*args, **kwargs):
|
|
captured.append(signature.bind(*args, **kwargs).arguments["layer_parameters"])
|
|
return "result.safetensors"
|
|
|
|
monkeypatch.setattr(module, name, process)
|
|
monkeypatch.setattr(module.folder_paths, "get_full_path_or_raise", lambda category, name: name)
|
|
if module_name in {"lora_extract_svd", "dora_extract_wd", "dora_learned_wd", "text_encoder_extract"}:
|
|
monkeypatch.setattr(module, "_build_lora_output_path", lambda name: (name, name))
|
|
if module_name == "lora_resize":
|
|
monkeypatch.setattr(module, "canonical_model_artifact_path", lambda category, name: (name, name))
|
|
for node in nodes(module):
|
|
values = {}
|
|
for item in node.define_schema().inputs:
|
|
value = getattr(item, "default", None)
|
|
if value is None:
|
|
options = getattr(item, "options", [])
|
|
value = options[0] if options else item.id
|
|
values[item.id] = value
|
|
values["layer_parameters"] = rules
|
|
if "execution_mode" in values:
|
|
values["execution_mode"] = "MERGE"
|
|
node.execute(**values)
|
|
assert len(captured) == count
|
|
assert all(value is rules for value in captured)
|
|
|
|
|
|
def test_no_expansion_to_other_node_families():
|
|
for module_name in ("consensus_merger", "cwb_model_graft", "cwb_delta_lora_merger", "lodestone_merger", "lora_merger", "model_analysis", "dtype_conversion"):
|
|
module = load(module_name)
|
|
for node in nodes(module):
|
|
assert "layer_parameters" not in {item.id for item in node.define_schema().inputs}
|
|
resize = load("lora_resize")
|
|
for node in (
|
|
resize.LoRAMergeToModel,
|
|
resize.LoRANormalizeAlpha,
|
|
resize.LoRAModifyAlpha,
|
|
resize.LoRADenormalizeAlpha,
|
|
):
|
|
assert "layer_parameters" not in {item.id for item in node.define_schema().inputs}
|