nodeslicer/tests/test_multi_groups.py
2026-08-25 14:47:46 +03:00

140 lines
4.5 KiB
Python

import unittest
from unittest.mock import MagicMock
import sys
from pathlib import Path
PROJECT_ROOT = Path(__file__).resolve().parent.parent
sys.path.append(str(PROJECT_ROOT.parent))
class DummyBase:
def _get_data_dict(self):
if not hasattr(self, "_data") or self._data is None:
self._data = {}
return self._data
def __getitem__(self, key):
return self._get_data_dict()[key]
def __setitem__(self, key, value):
self._get_data_dict()[key] = value
def __contains__(self, key):
return key in self._get_data_dict()
def get(self, key, default=None):
return self._get_data_dict().get(key, default)
mock_types = MagicMock()
mock_types.NodeSocket = type("NodeSocket", (DummyBase,), {})
mock_types.Node = DummyBase
mock_types.NodeCustomGroup = type("NodeCustomGroup", (DummyBase,), {})
mock_types.NodeTree = type("NodeTree", (DummyBase,), {})
mock_types.PropertyGroup = type("PropertyGroup", (DummyBase,), {})
mock_types.Operator = type("Operator", (DummyBase,), {})
mock_types.Panel = type("Panel", (DummyBase,), {})
mock_types.Menu = type("Menu", (DummyBase,), {})
mock_types.Object = type("Object", (DummyBase,), {})
mock_types.Mesh = type("Mesh", (DummyBase,), {})
mock_bpy = MagicMock()
mock_bpy.types = mock_types
sys.modules['bpy'] = mock_bpy
sys.modules['bpy.types'] = mock_types
sys.modules['bpy.props'] = MagicMock()
sys.modules['bpy.utils'] = MagicMock()
sys.modules['bpy.utils.previews'] = MagicMock()
sys.modules['bpy.app'] = MagicMock()
sys.modules['bpy.app.handlers'] = MagicMock()
# Socket and node testing mocks
class TestNodeSocket:
def __init__(self, name="", identifier="", node=None):
self.name = name
self.identifier = identifier or name
self.is_linked = False
self.links = []
self.default = None
self.default_value = ""
self.value = ""
self.hide = False
self.display_shape = ""
self.node = node
class TestSocketCollection(list):
def __init__(self, node=None):
super().__init__()
self.node = node
def new(self, type_name, name, identifier=""):
sock = TestNodeSocket(name, identifier or name, node=self.node)
self.append(sock)
return sock
def remove(self, sock):
if sock in self:
super().remove(sock)
def get(self, name, default=None):
for item in self:
if item.name == name or item.identifier == name:
return item
return default
from NodeSlicer.infra.blender.blender_slice_nodes_generic import SliceNode, resolve_input
from NodeSlicer.core.nodeslicer.nodes.gcode import SLICENODE_JoinGcode
class TestMultiGroupsManualValue(unittest.TestCase):
def test_join_gcode_multi_groups_manual_value(self):
node = SLICENODE_JoinGcode()
node.inputs = TestSocketCollection(node)
node.outputs = TestSocketCollection(node)
node.add_output("NodeSocketString", "Gcode")
node.add_multi_socket_group("NodeSocketString", name="Gcode")
# Initial state should have 1 input socket
self.assertEqual(len(node.inputs), 1)
# Entering a manual value in socket 0
node.inputs[0].value = "G1 X10 Y10 F1200"
node._update_multi_groups()
# Should trigger adding socket 1
self.assertEqual(len(node.inputs), 2)
# Entering a manual value in socket 1
node.inputs[1].value = "G1 X20 Y20"
node._update_multi_groups()
# Should trigger adding socket 2
self.assertEqual(len(node.inputs), 3)
# Evaluate output should join manual values
joined = node.evaluate_output("Gcode")
self.assertEqual(joined, "G1 X10 Y10 F1200\nG1 X20 Y20")
# Clearing socket 1 manual value
node.inputs[1].value = ""
node._update_multi_groups()
# Trailing inactive sockets cleaned up
self.assertEqual(len(node.inputs), 2)
def test_join_gcode_default_value_trigger(self):
node = SLICENODE_JoinGcode()
node.inputs = TestSocketCollection(node)
node.outputs = TestSocketCollection(node)
node.add_output("NodeSocketString", "Gcode")
node.add_multi_socket_group("NodeSocketString", name="Gcode")
# Set default_value on native socket
node.inputs[0].default_value = "M104 S200"
node._update_multi_groups()
self.assertEqual(len(node.inputs), 2)
self.assertEqual(node.evaluate_output("Gcode"), "M104 S200")
if __name__ == "__main__":
unittest.main()