diff --git a/src/capellambse_context_diagrams/__init__.py b/src/capellambse_context_diagrams/__init__.py index 0fbda150..256c5c49 100644 --- a/src/capellambse_context_diagrams/__init__.py +++ b/src/capellambse_context_diagrams/__init__.py @@ -26,7 +26,6 @@ import capellambse.model as m from capellambse.diagram import COLORS, CSSdef, capstyle from capellambse.metamodel import cs, fa, information, la, oa, pa, sa -from capellambse.model import DiagramType from . import _elkjs, context, styling @@ -38,7 +37,7 @@ DefaultRenderParams = dict[str, t.Any] SupportedContextClass = tuple[ - type[m.ModelElement], DiagramType, DefaultRenderParams + type[m.ModelElement], m.DiagramType, DefaultRenderParams ] SupportedInterfaceContextClass = tuple[ type[m.ModelElement], dict[type[m.ModelElement], str], DefaultRenderParams @@ -75,28 +74,29 @@ def init() -> None: register_data_flow_view() register_cable_tree_view() register_custom_diagram() + register_diagram_layout_accessor() # register_functional_context() XXX: Future def register_classes() -> None: """Add the `context_diagram` property to the relevant model objects.""" supported_classes: list[SupportedContextClass] = [ - (oa.Entity, DiagramType.OAB, {}), + (oa.Entity, m.DiagramType.OAB, {}), ( oa.OperationalActivity, - DiagramType.OAB, + m.DiagramType.OAB, {"display_parent_relation": True}, ), - (oa.OperationalCapability, DiagramType.OCB, {}), - (sa.Mission, DiagramType.MCB, {}), + (oa.OperationalCapability, m.DiagramType.OCB, {}), + (sa.Mission, m.DiagramType.MCB, {}), ( sa.Capability, - DiagramType.MCB, + m.DiagramType.MCB, {"display_symbols_as_boxes": False}, ), ( sa.SystemComponent, - DiagramType.SAB, + m.DiagramType.SAB, { "display_symbols_as_boxes": True, "display_parent_relation": True, @@ -106,7 +106,7 @@ def register_classes() -> None: ), ( sa.SystemFunction, - DiagramType.SAB, + m.DiagramType.SAB, { "display_symbols_as_boxes": True, "display_parent_relation": True, @@ -115,7 +115,7 @@ def register_classes() -> None: ), ( la.LogicalComponent, - DiagramType.LAB, + m.DiagramType.LAB, { "display_symbols_as_boxes": True, "display_parent_relation": True, @@ -125,7 +125,7 @@ def register_classes() -> None: ), ( la.LogicalFunction, - DiagramType.LAB, + m.DiagramType.LAB, { "display_symbols_as_boxes": True, "display_parent_relation": True, @@ -134,7 +134,7 @@ def register_classes() -> None: ), ( pa.PhysicalComponent, - DiagramType.PAB, + m.DiagramType.PAB, { "display_parent_relation": True, "display_port_labels": True, @@ -143,7 +143,7 @@ def register_classes() -> None: ), ( pa.PhysicalFunction, - DiagramType.PAB, + m.DiagramType.PAB, { "display_parent_relation": True, }, @@ -178,32 +178,32 @@ def register_interface_context() -> None: ( oa.CommunicationMean, { - oa.EntityPkg: DiagramType.OAB.value, - oa.Entity: DiagramType.OAB.value, + oa.EntityPkg: m.DiagramType.OAB.value, + oa.Entity: m.DiagramType.OAB.value, }, {"include_interface": True}, ), ( fa.ComponentExchange, { - sa.SystemComponentPkg: DiagramType.SAB.value, - sa.SystemComponent: DiagramType.SAB.value, - la.LogicalComponentPkg: DiagramType.LAB.value, - la.LogicalComponent: DiagramType.LAB.value, - pa.PhysicalComponentPkg: DiagramType.PAB.value, - pa.PhysicalComponent: DiagramType.PAB.value, + sa.SystemComponentPkg: m.DiagramType.SAB.value, + sa.SystemComponent: m.DiagramType.SAB.value, + la.LogicalComponentPkg: m.DiagramType.LAB.value, + la.LogicalComponent: m.DiagramType.LAB.value, + pa.PhysicalComponentPkg: m.DiagramType.PAB.value, + pa.PhysicalComponent: m.DiagramType.PAB.value, }, {"include_interface": True, "include_port_allocations": True}, ), ( cs.PhysicalLink, { - sa.SystemComponentPkg: DiagramType.SAB.value, - sa.SystemComponent: DiagramType.SAB.value, - la.LogicalComponentPkg: DiagramType.LAB.value, - la.LogicalComponent: DiagramType.LAB.value, - pa.PhysicalComponentPkg: DiagramType.PAB.value, - pa.PhysicalComponent: DiagramType.PAB.value, + sa.SystemComponentPkg: m.DiagramType.SAB.value, + sa.SystemComponent: m.DiagramType.SAB.value, + la.LogicalComponentPkg: m.DiagramType.LAB.value, + la.LogicalComponent: m.DiagramType.LAB.value, + pa.PhysicalComponentPkg: m.DiagramType.PAB.value, + pa.PhysicalComponent: m.DiagramType.PAB.value, }, {"include_interface": True, "display_port_labels": True}, ), @@ -227,7 +227,7 @@ def register_interface_context() -> None: "stroke-dasharray": "2", "text_fill": COLORS["black"], } - for dt in (DiagramType.SAB, DiagramType.LAB, DiagramType.PAB): + for dt in (m.DiagramType.SAB, m.DiagramType.LAB, m.DiagramType.PAB): capstyle.STYLES[dt.value]["Edge.PortInputAllocation"] = ( port_alloc_input_style ) @@ -244,11 +244,11 @@ def register_functional_context() -> None: The functional context diagrams will be available soon. """ attr_name = f"functional_{ATTR_NAME}" - supported_classes: list[tuple[type[m.ModelElement], DiagramType]] = [ - (oa.Entity, DiagramType.OAB), - (sa.SystemComponent, DiagramType.SAB), - (la.LogicalComponent, DiagramType.LAB), - (pa.PhysicalComponent, DiagramType.PAB), + supported_classes: list[tuple[type[m.ModelElement], m.DiagramType]] = [ + (oa.Entity, m.DiagramType.OAB), + (sa.SystemComponent, m.DiagramType.SAB), + (la.LogicalComponent, m.DiagramType.LAB), + (pa.PhysicalComponent, m.DiagramType.PAB), ] class_: type[m.ModelElement] for class_, dgcls in supported_classes: @@ -264,7 +264,7 @@ def register_physical_port_context() -> None: m.set_accessor( cs.PhysicalPort, ATTR_NAME, - context.PhysicalPortContextAccessor(DiagramType.PAB.value, {}), + context.PhysicalPortContextAccessor(m.DiagramType.PAB.value, {}), ) @@ -273,7 +273,7 @@ def register_tree_view() -> None: m.set_accessor( information.Class, "tree_view", - context.ClassTreeAccessor(DiagramType.CDB.value), + context.ClassTreeAccessor(m.DiagramType.CDB.value), ) @@ -284,14 +284,14 @@ def register_realization_view() -> None: of all layers. """ supported_classes: list[SupportedContextClass] = [ - (oa.Entity, DiagramType.OAB, {}), - (oa.OperationalActivity, DiagramType.OAIB, {}), - (sa.SystemComponent, DiagramType.SAB, {}), - (sa.SystemFunction, DiagramType.SDFB, {}), - (la.LogicalComponent, DiagramType.LAB, {}), - (la.LogicalFunction, DiagramType.LDFB, {}), - (pa.PhysicalComponent, DiagramType.PAB, {}), - (pa.PhysicalFunction, DiagramType.PDFB, {}), + (oa.Entity, m.DiagramType.OAB, {}), + (oa.OperationalActivity, m.DiagramType.OAIB, {}), + (sa.SystemComponent, m.DiagramType.SAB, {}), + (sa.SystemFunction, m.DiagramType.SDFB, {}), + (la.LogicalComponent, m.DiagramType.LAB, {}), + (la.LogicalFunction, m.DiagramType.LDFB, {}), + (pa.PhysicalComponent, m.DiagramType.PAB, {}), + (pa.PhysicalFunction, m.DiagramType.PDFB, {}), ] styles: dict[str, dict[str, capstyle.CSSdef]] = {} for class_, dgcls, _ in supported_classes: @@ -315,8 +315,8 @@ def register_realization_view() -> None: def register_data_flow_view() -> None: supported_classes: list[SupportedContextClass] = [ - (oa.OperationalCapability, DiagramType.OAIB, {}), # portless - (sa.Capability, DiagramType.SDFB, {}), # default + (oa.OperationalCapability, m.DiagramType.OAIB, {}), # portless + (sa.Capability, m.DiagramType.SDFB, {}), # default ] class_: type[m.ModelElement] for class_, dgcls, default_render_params in supported_classes: @@ -330,7 +330,7 @@ def register_cable_tree_view() -> None: cs.PhysicalLink, "cable_tree", context.CableTreeAccessor( - DiagramType.PAB.value, + m.DiagramType.PAB.value, {}, ), ) @@ -338,23 +338,23 @@ def register_cable_tree_view() -> None: def register_custom_diagram() -> None: """Add the `custom_diagram` attribute to `ModelObject`s.""" - supported_classes: list[tuple[type[m.ModelElement], DiagramType]] = [ - (oa.Entity, DiagramType.OAB), - (oa.OperationalActivity, DiagramType.OAB), - (oa.OperationalCapability, DiagramType.OCB), - (oa.CommunicationMean, DiagramType.OAB), - (sa.Mission, DiagramType.MCB), - (sa.Capability, DiagramType.MCB), - (sa.SystemComponent, DiagramType.SAB), - (sa.SystemFunction, DiagramType.SAB), - (la.LogicalComponent, DiagramType.LAB), - (la.LogicalFunction, DiagramType.LAB), - (pa.PhysicalComponent, DiagramType.PAB), - (pa.PhysicalFunction, DiagramType.PAB), - (cs.PhysicalLink, DiagramType.PAB), - (cs.PhysicalPort, DiagramType.PAB), - (fa.ComponentExchange, DiagramType.SAB), - (information.Class, DiagramType.CDB), + supported_classes: list[tuple[type[m.ModelElement], m.DiagramType]] = [ + (oa.Entity, m.DiagramType.OAB), + (oa.OperationalActivity, m.DiagramType.OAB), + (oa.OperationalCapability, m.DiagramType.OCB), + (oa.CommunicationMean, m.DiagramType.OAB), + (sa.Mission, m.DiagramType.MCB), + (sa.Capability, m.DiagramType.MCB), + (sa.SystemComponent, m.DiagramType.SAB), + (sa.SystemFunction, m.DiagramType.SAB), + (la.LogicalComponent, m.DiagramType.LAB), + (la.LogicalFunction, m.DiagramType.LAB), + (pa.PhysicalComponent, m.DiagramType.PAB), + (pa.PhysicalFunction, m.DiagramType.PAB), + (cs.PhysicalLink, m.DiagramType.PAB), + (cs.PhysicalPort, m.DiagramType.PAB), + (fa.ComponentExchange, m.DiagramType.SAB), + (information.Class, m.DiagramType.CDB), ] for class_, dgcls in supported_classes: m.set_accessor( @@ -362,3 +362,18 @@ def register_custom_diagram() -> None: "custom_diagram", context.CustomAccessor(dgcls.value, {}), ) + + +def register_diagram_layout_accessor() -> None: + """Add the `auto_layout` attribute to `Diagram`s.""" + render_params = { + m.DiagramType.SAB: {"display_symbols_as_boxes": True}, + m.DiagramType.LAB: {"display_symbols_as_boxes": True}, + m.DiagramType.PAB: {"display_port_labels": True}, + } + + m.set_accessor( + m.Diagram, + "auto_layout", + context.DiagramLayoutAccessor(render_params), + ) diff --git a/src/capellambse_context_diagrams/collectors/diagram_view.py b/src/capellambse_context_diagrams/collectors/diagram_view.py new file mode 100644 index 00000000..457e5ade --- /dev/null +++ b/src/capellambse_context_diagrams/collectors/diagram_view.py @@ -0,0 +1,193 @@ +# SPDX-FileCopyrightText: Copyright DB InfraGO AG and the capellambse-context-diagrams contributors +# SPDX-License-Identifier: Apache-2.0 + +# SPDX-FileCopyrightText: 2022 Copyright DB InfraGO AG and the capellambse-context-diagrams contributors +# SPDX-License-Identifier: Apache-2.0 +"""Collector for the DiagramView. + +Functionality for collecting all model elements from a [`Diagram`][capellambse.model.diagram.Diagram] +and conversion of it into [`_elkjs.ELKInputData`][capellambse_context_diagrams._elkjs.ELKInputData] +for an automated layout. +""" + +from __future__ import annotations + +import logging +import typing as t + +from capellambse import model as m +from capellambse.metamodel import cs, fa + +from .. import _elkjs, context +from . import generic, makers + +logger = logging.getLogger(__name__) +PortTypes: t.TypeAlias = fa.FunctionPort | fa.ComponentPort | cs.PhysicalPort + + +def is_function(node: m.ModelElement) -> bool: + """Check if the ``node`` is a function.""" + return isinstance(node, fa.Function) + + +def is_part(node: m.ModelElement) -> bool: + """Check if the ``node`` is a part.""" + return node.xtype.endswith("Part") + + +def is_exchange(node: m.ModelElement) -> bool: + """Check if the ``node`` is an exchange.""" + return hasattr(node, "source") and hasattr(node, "target") + + +def is_allocation(node: m.ModelElement) -> bool: + """Check if the ``node`` is an allocation.""" + return node.xtype.endswith("PortAllocation") + + +def is_port(node: m.ModelElement) -> bool: + """Check if the ``node`` is a port.""" + return node.xtype.endswith("Port") + + +class Collector: + """Collects all model elements from a diagram for ELK.""" + + def __init__(self, diagram: context.ELKDiagram): + self.diagram = diagram + self._diagram = diagram.target + self.data = generic.collector(self.diagram, no_symbol=True) + self.data.children = [] + + self.made_elements: dict[ + str, + _elkjs.ELKInputChild | _elkjs.ELKInputEdge | _elkjs.ELKInputPort, + ] = {} + self.made_boxes: dict[str, _elkjs.ELKInputChild] = {} + self.made_ports: dict[str, _elkjs.ELKInputPort] = {} + self.exchanges: dict[str, fa.AbstractExchange] = {} + self.global_boxes: dict[str, _elkjs.ELKInputChild] = {} + self.ports: dict[str, PortTypes] = {} + self.boxes_to_delete: set[str] = set() + + def __call__(self, params: dict[str, t.Any]) -> _elkjs.ELKInputData: + self._get_data(params) + self._adjust_box_sizes(params) + self._solve_hierarchy(params) + return self.data + + def _get_data(self, params: dict[str, t.Any]): + del params # No use for it now + for node in self._diagram.nodes: + if is_function(node): + self.make_all_owner_boxes(node) + elif is_part(node): + self.make_all_owner_boxes(node.type) + elif is_exchange(node) and not is_allocation(node): + self.exchanges[node.uuid] = node # type: ignore[assignment] + edge = _elkjs.ELKInputEdge( + id=node.uuid, + sources=[node.source.uuid], + targets=[node.target.uuid], + labels=makers.make_label( + node.name, max_width=makers.MAX_LABEL_WIDTH + ), + ) + self.ports[node.source.uuid] = node.source + self.ports[node.target.uuid] = node.target + self.data.edges.append(edge) + elif is_port(node): + self.made_ports[node.uuid] = (port := self._make_port(node)) + self.made_boxes[node.owner.uuid].ports.append(port) + + if leftover_ports := set(self.ports) - set(self.made_ports): + logger.debug( + "There are ports missing in the diagram nodes: %r", + leftover_ports, + ) + for uuid in leftover_ports: + port_obj = self.ports[uuid] + self.made_ports[uuid] = (port := self._make_port(port_obj)) + self.made_boxes[port_obj.owner.uuid].ports.append(port) + + for uuid in self.boxes_to_delete: + del self.global_boxes[uuid] + + self.data.children.extend(self.global_boxes.values()) + + def make_all_owner_boxes(self, obj: m.ModelElement): + """Make boxes for all owners of the given object. + + Notes + ----- + Also makes a box for the object itself. + """ + if not (obj_box := self.made_boxes.get(obj.uuid)): + obj_box = self._make_box(obj, no_symbol=True, slim_width=False) + self.made_boxes[obj.uuid] = obj_box + + current: m.ModelElement | None = obj + while ( + current + and hasattr(current, "owner") + and not isinstance(current.owner, generic.PackageTypes) + ): + current = self._make_owner_box(current) + + def _make_owner_box(self, obj: m.ModelElement) -> m.ModelElement | None: + if obj.owner.uuid in self.diagram._hide_elements: + return None + + if not (parent_box := self.made_boxes.get(obj.owner.uuid)): + parent_box = self._make_box( + obj.owner, + no_symbol=True, + layout_options=makers.DEFAULT_LABEL_LAYOUT_OPTIONS, + ) + assert (obj_box := self.made_boxes.get(obj.uuid)) + for box in (children := parent_box.children): + if box.id == obj.uuid: + box = obj_box + break + else: + children.append(obj_box) + for label in parent_box.labels: + label.layoutOptions = makers.DEFAULT_LABEL_LAYOUT_OPTIONS + + self.boxes_to_delete.add(obj.uuid) + return obj.owner + + def _make_box( + self, obj: m.ModelElement, **kwargs: t.Any + ) -> _elkjs.ELKInputChild: + box = makers.make_box(obj, **kwargs) + self.global_boxes[obj.uuid] = box + self.made_boxes[obj.uuid] = box + return box + + def _make_port(self, obj: m.ModelElement) -> _elkjs.ELKInputPort: + if self.diagram._display_port_labels: + label = obj.name or "UNKNOWN" + else: + label = "" + + return makers.make_port(obj.uuid, label=label) + + def _adjust_box_sizes(self, params: dict[str, t.Any]): + del params # No use for it now + for box in self.made_boxes.values(): + box.height += (makers.PORT_SIZE + 2 * makers.PORT_PADDING) * ( + len(box.ports) + 1 + ) + + def _solve_hierarchy(self, params: dict[str, t.Any]): + del params # No use for it now + generic.move_edges(self.made_boxes, self.exchanges.values(), self.data) + + +def collect_from_diagram( + diagram: context.ELKDiagram, params: dict[str, t.Any] +) -> _elkjs.ELKInputData: + """Return ``ELKInputData`` from a diagram.""" + diagram._slim_center_box = False + return Collector(diagram)(params) diff --git a/src/capellambse_context_diagrams/collectors/generic.py b/src/capellambse_context_diagrams/collectors/generic.py index d7cb7566..6e67fcd8 100644 --- a/src/capellambse_context_diagrams/collectors/generic.py +++ b/src/capellambse_context_diagrams/collectors/generic.py @@ -60,7 +60,7 @@ def collector( - diagram: context.ContextDiagram, + diagram: context.CustomDiagram, *, width: int | float = makers.EOI_WIDTH, no_symbol: bool = False, diff --git a/src/capellambse_context_diagrams/collectors/makers.py b/src/capellambse_context_diagrams/collectors/makers.py index 29fbdf75..f3ddba40 100644 --- a/src/capellambse_context_diagrams/collectors/makers.py +++ b/src/capellambse_context_diagrams/collectors/makers.py @@ -214,7 +214,7 @@ def is_symbol(obj: str | m.ModelElement | None) -> bool: return type(obj).__name__ in BOX_TO_SYMBOL -def make_port(uuid: str) -> _elkjs.ELKInputPort: +def make_port(uuid: str, label: str = "") -> _elkjs.ELKInputPort: """Return a port. See Also @@ -222,9 +222,11 @@ def make_port(uuid: str) -> _elkjs.ELKInputPort: [`ELKInputPort`][capellambse_context_diagrams._elkjs.ELKInputPort] : Input data for an ELK port. """ + labels = make_label(label) if label else [] return _elkjs.ELKInputPort( id=uuid, width=PORT_SIZE, height=PORT_SIZE, + labels=labels, layoutOptions={"borderOffset": -4 * PORT_PADDING}, ) diff --git a/src/capellambse_context_diagrams/context.py b/src/capellambse_context_diagrams/context.py index 85e5a91c..b8bb6033 100644 --- a/src/capellambse_context_diagrams/context.py +++ b/src/capellambse_context_diagrams/context.py @@ -24,6 +24,7 @@ cable_tree, custom, dataflow_view, + diagram_view, exchanges, get_elkdata, realization_view, @@ -239,6 +240,47 @@ def __get__( # type: ignore return self._get(obj, CableTreeViewDiagram) +class DiagramLayoutAccessor(m.Accessor): + """Provides access to the context diagrams.""" + + def __init__( + self, + dgls_to_render_params: ( + dict[m.DiagramType, dict[str, t.Any]] | None + ) = None, + ) -> None: + super().__init__() + self._dgls_to_render_params = dgls_to_render_params or {} + + @t.overload + def __get__( + self, obj: None, objtype: type[t.Any] + ) -> DiagramLayoutAccessor: ... + @t.overload + def __get__( + self, obj: m.T, objtype: type[m.T] | None = None + ) -> ELKDiagram: ... + def __get__( + self, obj: m.T | None, objtype: type | None = None + ) -> m.Accessor | ELKDiagram: + """Make a ContextDiagram for the given model object.""" + del objtype + if obj is None: # pragma: no cover + return self + assert isinstance(obj, m.Diagram) + return self._get(obj) + + def _get(self, obj: m.Diagram) -> m.Accessor | ELKDiagram: + default_render_params = self._dgls_to_render_params.get(obj.type, {}) + new_diagram = ELKDiagram( + obj.type.value, + obj, + default_render_parameters=default_render_params, + ) + new_diagram.filters.add(filters.NO_UUID) + return new_diagram + + class CustomDiagram(m.AbstractDiagram): """An automatically generated custom diagram. @@ -309,13 +351,13 @@ class CustomDiagram(m.AbstractDiagram): def __init__( self, class_: str, - obj: m.ModelElement, + obj: m.ModelElement | m.Diagram, *, render_styles: dict[str, styling.Styler] | None = None, default_render_parameters: dict[str, t.Any], ) -> None: super().__init__(obj._model) - self.target = obj # type: ignore[misc] + self.target = obj # type: ignore[misc,assignment] self.styleclass = class_ self.render_styles = render_styles or {} @@ -576,14 +618,14 @@ def _yield_port_allocations( self, node: _elkjs.ELKOutputNode, interface_port: _elkjs.ELKOutputPort ) -> cabc.Iterator[_elkjs.ELKOutputEdge]: ref = cdiagram.Vector2D(node.position.x, node.position.y) + interface_middle = _calculate_middle( + interface_port.position, interface_port.size, node.position + ) for position, port in _get_all_ports(node, ref=ref): if port == interface_port: continue port_middle = _calculate_middle(position, port.size) - interface_middle = _calculate_middle( - interface_port.position, interface_port.size, node.position - ) styleclass = self.serializer.get_styleclass(port.id) if styleclass in {"FIP", "FOP"}: yield _create_edge( @@ -977,6 +1019,80 @@ def _collector( self.collector = custom.collector +class ELKDiagram(CustomDiagram): + """A former diagram layouted by ELKJS.""" + + _include_port_allocations: bool + _hide_elements: set[str] + + def __init__( + self, + class_: str, + obj: m.Diagram, + *, + render_styles: dict[str, styling.Styler] | None = None, + default_render_parameters: dict[str, t.Any], + ) -> None: + default_render_parameters = { + "include_port_allocations": True, + "hide_elements": set(), + } | default_render_parameters + super().__init__( + class_, + obj, + render_styles=render_styles, + default_render_parameters=default_render_parameters, + ) + self.collector = diagram_view.collect_from_diagram + self.target: m.Diagram = obj + + # self.__port_allocations = [] + self.__nodes: m.MixedElementList | None = None + + @property + def uuid(self) -> str: + """Returns diagram UUID.""" + return f"{self.target.uuid}_elk" + + @property + def name(self) -> str: + """Returns the diagram name.""" + return f"ELK layout of {self.target.name.replace('/', '- or -')}" + + @property + def nodes(self) -> m.MixedElementList: + """Return a list of all nodes visible in this diagram.""" + if not self.__nodes: + self.__nodes = super().nodes + assert self.__nodes is not None + return self.__nodes + + def _create_diagram(self, params: dict[str, t.Any]) -> cdiagram.Diagram: + data = self.elk_input_data(params) + assert not isinstance(data, tuple) + layout = try_to_layout(data) + # if self._include_port_allocations: + # self._add_port_allocations(layout) + + add_context(layout) + return self.serializer.make_diagram( + layout, transparent_background=self._transparent_background + ) + + # def _add_port_allocations(self, layout: _elkjs.ELKOutputData) -> None: + # for allocation in self.__port_allocations: + # if isinstance(allocation.source, fa.ComponentPort): + # interface_port = allocation.source + # port = allocation.target + # else: + # interface_port = allocation.target + # port = allocation.source + + # allocation.source + + # allocation.target + + def try_to_layout(data: _elkjs.ELKInputData) -> _elkjs.ELKOutputData: """Try calling elkjs, raise a JSONDecodeError if it fails.""" try: @@ -1070,7 +1186,7 @@ def calculate_label_position( def _get_all_ports( node: _elkjs.ELKOutputNode, ref: cdiagram.Vector2D ) -> cabc.Iterator[tuple[cdiagram.Vector2D, _elkjs.ELKOutputPort]]: - """Yield all ports from.""" + """Yield all ports from a given ``node`` and its children.""" for child in node.children: if isinstance(child, _elkjs.ELKOutputPort): yield ref + (child.position.x, child.position.y), child diff --git a/tests/data/ContextDiagram.afm b/tests/data/ContextDiagram.afm index 02f4486c..ca9e0093 100644 --- a/tests/data/ContextDiagram.afm +++ b/tests/data/ContextDiagram.afm @@ -1,6 +1,6 @@ - - - + + + diff --git a/tests/data/ContextDiagram.aird b/tests/data/ContextDiagram.aird index ed0d1a45..c717e122 100644 --- a/tests/data/ContextDiagram.aird +++ b/tests/data/ContextDiagram.aird @@ -1,6 +1,6 @@ - - + + ContextDiagram.afm ContextDiagram.capella @@ -8,14 +8,14 @@ - + - +
@@ -23,7 +23,7 @@ - +
@@ -31,7 +31,7 @@ - +
@@ -39,7 +39,7 @@ - +
@@ -53,15 +53,15 @@ - + - + - + @@ -71,30 +71,30 @@ - + - + - + - + - + - +
@@ -102,7 +102,7 @@ - +
@@ -110,7 +110,7 @@ - +
@@ -118,7 +118,7 @@ - +
@@ -126,7 +126,7 @@ - +
@@ -134,7 +134,7 @@ - +
@@ -142,7 +142,7 @@ - +
@@ -150,7 +150,7 @@ - +
@@ -158,7 +158,7 @@ - +
@@ -166,11 +166,11 @@ - + - +
@@ -178,7 +178,7 @@ - +
@@ -186,7 +186,7 @@ - +
@@ -908,7 +908,7 @@ - + @@ -917,7 +917,7 @@ - + @@ -938,7 +938,7 @@ - + @@ -950,7 +950,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -959,7 +959,7 @@ - + @@ -968,7 +968,7 @@ - + @@ -987,7 +987,7 @@ - + @@ -999,7 +999,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -1008,12 +1008,12 @@ - + - + @@ -1025,7 +1025,7 @@ - + @@ -1034,7 +1034,7 @@ - + @@ -1043,7 +1043,7 @@ - + @@ -1051,7 +1051,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -1062,7 +1062,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -1079,7 +1079,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -1088,7 +1088,7 @@ - + @@ -1107,7 +1107,7 @@ - + @@ -1116,7 +1116,7 @@ - + @@ -1124,7 +1124,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -1138,7 +1138,7 @@ - + @@ -1147,7 +1147,7 @@ - + @@ -1169,7 +1169,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -1181,7 +1181,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -1190,7 +1190,7 @@ - + @@ -1199,12 +1199,12 @@ - + - + @@ -1218,7 +1218,7 @@ - + @@ -1227,7 +1227,7 @@ - + @@ -1246,7 +1246,7 @@ - + @@ -1254,7 +1254,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -1767,7 +1767,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -1778,7 +1778,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -1811,7 +1811,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -1822,7 +1822,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -1833,7 +1833,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -1844,7 +1844,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -1855,7 +1855,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -1866,7 +1866,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -1877,7 +1877,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -2584,7 +2584,7 @@ - + @@ -2593,7 +2593,7 @@ - + @@ -2615,7 +2615,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -2624,7 +2624,7 @@ - + @@ -2633,7 +2633,7 @@ - + @@ -2641,7 +2641,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -2653,7 +2653,7 @@ - + @@ -2662,7 +2662,7 @@ - + @@ -2670,7 +2670,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -2682,7 +2682,7 @@ - + @@ -2690,7 +2690,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -2704,7 +2704,7 @@ - + @@ -2723,7 +2723,7 @@ - + @@ -2732,7 +2732,7 @@ - + @@ -2741,7 +2741,7 @@ - + @@ -2750,7 +2750,7 @@ - + @@ -2759,12 +2759,12 @@ - + - + @@ -2927,7 +2927,7 @@ - + @@ -2943,12 +2943,12 @@ - + - + @@ -3522,7 +3522,7 @@ - + @@ -3531,7 +3531,7 @@ - + @@ -3550,7 +3550,7 @@ - + @@ -3559,7 +3559,7 @@ - + @@ -3568,7 +3568,7 @@ - + @@ -3577,7 +3577,7 @@ - + @@ -3586,7 +3586,7 @@ - + @@ -3595,7 +3595,7 @@ - + @@ -3604,7 +3604,7 @@ - + @@ -3612,7 +3612,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + italic @@ -3627,7 +3627,7 @@ - + @@ -3636,7 +3636,7 @@ - + @@ -3648,7 +3648,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -3667,7 +3667,7 @@ - + @@ -3676,7 +3676,7 @@ - + @@ -3684,7 +3684,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -3744,7 +3744,7 @@ - + @@ -3753,7 +3753,7 @@ - + @@ -3762,7 +3762,7 @@ - + @@ -3781,7 +3781,7 @@ - + @@ -3790,7 +3790,7 @@ - + @@ -3799,7 +3799,7 @@ - + @@ -3808,7 +3808,7 @@ - + @@ -3816,7 +3816,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -5741,7 +5741,7 @@ - + @@ -5750,7 +5750,7 @@ - + @@ -5759,7 +5759,7 @@ - + @@ -5768,7 +5768,7 @@ - + @@ -5777,7 +5777,7 @@ - + @@ -5786,7 +5786,7 @@ - + @@ -5795,7 +5795,7 @@ - + @@ -5804,7 +5804,7 @@ - + @@ -5813,7 +5813,7 @@ - + @@ -5822,7 +5822,7 @@ - + @@ -5850,7 +5850,7 @@ - + @@ -5859,7 +5859,7 @@ - + @@ -5868,7 +5868,7 @@ - + @@ -5877,7 +5877,7 @@ - + @@ -5886,7 +5886,7 @@ - + @@ -5895,7 +5895,7 @@ - + @@ -5904,7 +5904,7 @@ - + @@ -5912,7 +5912,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -5926,7 +5926,7 @@ - + @@ -5935,7 +5935,7 @@ - + @@ -5947,7 +5947,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -5956,7 +5956,7 @@ - + @@ -5965,7 +5965,7 @@ - + @@ -5974,7 +5974,7 @@ - + @@ -5983,7 +5983,7 @@ - + @@ -5992,7 +5992,7 @@ - + @@ -6013,7 +6013,7 @@ - + @@ -6022,7 +6022,7 @@ - + @@ -6031,7 +6031,7 @@ - + @@ -6040,7 +6040,7 @@ - + @@ -6049,7 +6049,7 @@ - + @@ -6058,7 +6058,7 @@ - + @@ -6067,7 +6067,7 @@ - + @@ -6076,7 +6076,7 @@ - + @@ -6085,7 +6085,7 @@ - + @@ -6094,7 +6094,7 @@ - + @@ -6103,7 +6103,7 @@ - + @@ -6112,7 +6112,7 @@ - + @@ -6121,7 +6121,7 @@ - + @@ -6130,7 +6130,7 @@ - + @@ -6139,7 +6139,7 @@ - + @@ -6148,7 +6148,7 @@ - + @@ -6157,7 +6157,7 @@ - + @@ -6169,7 +6169,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -6206,7 +6206,7 @@ - + @@ -6215,7 +6215,7 @@ - + @@ -6224,7 +6224,7 @@ - + @@ -6233,7 +6233,7 @@ - + @@ -6242,7 +6242,7 @@ - + @@ -6251,7 +6251,7 @@ - + @@ -6260,7 +6260,7 @@ - + @@ -6268,7 +6268,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -8084,7 +8084,7 @@ - + @@ -8093,7 +8093,7 @@ - + @@ -8105,7 +8105,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -8114,7 +8114,7 @@ - + @@ -8134,7 +8134,7 @@ - + @@ -8143,7 +8143,7 @@ - + @@ -8164,7 +8164,7 @@ - + @@ -8186,7 +8186,7 @@ - + @@ -8204,7 +8204,7 @@ - + @@ -8225,7 +8225,7 @@ - + @@ -8246,7 +8246,7 @@ - + @@ -9204,7 +9204,7 @@ - + @@ -9223,7 +9223,7 @@ - + @@ -9231,7 +9231,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -9243,7 +9243,7 @@ - + @@ -9251,7 +9251,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -9757,7 +9757,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -9768,7 +9768,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -9790,7 +9790,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -9801,7 +9801,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -9812,7 +9812,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -9823,7 +9823,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -9834,7 +9834,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -9845,7 +9845,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -9866,7 +9866,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -9877,7 +9877,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -9900,7 +9900,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -9988,7 +9988,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -9999,7 +9999,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -10010,7 +10010,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -10021,7 +10021,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -10933,7 +10933,7 @@ - + @@ -10942,7 +10942,7 @@ - + @@ -10954,7 +10954,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -10963,7 +10963,7 @@ - + @@ -10983,7 +10983,7 @@ - + @@ -10992,7 +10992,7 @@ - + @@ -11011,7 +11011,7 @@ - + @@ -11020,12 +11020,12 @@ - + - + @@ -11038,7 +11038,7 @@ - + @@ -11047,7 +11047,7 @@ - + @@ -11056,7 +11056,7 @@ - + @@ -11064,7 +11064,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -11077,7 +11077,7 @@ - + @@ -11086,7 +11086,7 @@ - + @@ -11109,7 +11109,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -11128,7 +11128,7 @@ - + @@ -11137,7 +11137,7 @@ - + @@ -11146,7 +11146,7 @@ - + @@ -11155,12 +11155,12 @@ - + - + @@ -11197,7 +11197,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -11216,7 +11216,7 @@ - + @@ -11225,7 +11225,7 @@ - + @@ -11233,7 +11233,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -11250,7 +11250,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -11269,7 +11269,7 @@ - + @@ -11277,7 +11277,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -11409,7 +11409,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -11428,7 +11428,7 @@ - + @@ -11437,12 +11437,12 @@ - + - + @@ -11833,7 +11833,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -11855,7 +11855,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -11864,7 +11864,7 @@ - + @@ -11872,7 +11872,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -11884,7 +11884,7 @@ - + @@ -11893,7 +11893,7 @@ - + @@ -11901,7 +11901,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -11925,7 +11925,7 @@ - + @@ -11933,7 +11933,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -11947,7 +11947,7 @@ - + @@ -11969,7 +11969,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -11977,7 +11977,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -12001,7 +12001,7 @@ - + @@ -12009,7 +12009,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -12036,7 +12036,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -12044,7 +12044,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -12361,7 +12361,7 @@ - + @@ -12759,7 +12759,7 @@ - + @@ -12789,7 +12789,7 @@ - + @@ -12798,7 +12798,7 @@ - + @@ -12806,7 +12806,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -12819,7 +12819,7 @@ - + @@ -12828,7 +12828,7 @@ - + @@ -12840,12 +12840,12 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + - + @@ -12859,7 +12859,7 @@ - + @@ -12900,7 +12900,7 @@ - + @@ -12908,7 +12908,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -12921,7 +12921,7 @@ - + @@ -12930,7 +12930,7 @@ - + @@ -12938,7 +12938,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -12951,7 +12951,7 @@ - + @@ -12960,7 +12960,7 @@ - + @@ -12969,7 +12969,7 @@ - + @@ -12977,7 +12977,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + italic @@ -12990,7 +12990,7 @@ - + @@ -12998,7 +12998,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -13010,7 +13010,7 @@ - + @@ -13018,7 +13018,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -13585,7 +13585,7 @@ - + @@ -13623,7 +13623,7 @@ - + @@ -14255,7 +14255,7 @@ - + @@ -14561,7 +14561,7 @@ - + @@ -14608,7 +14608,7 @@ - + @@ -14635,7 +14635,7 @@ - + @@ -14644,7 +14644,7 @@ - + @@ -14664,7 +14664,7 @@ - + @@ -14673,7 +14673,7 @@ - + @@ -14681,7 +14681,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -14704,7 +14704,7 @@ - + @@ -14716,7 +14716,7 @@ - + @@ -14728,7 +14728,7 @@ - + @@ -14755,7 +14755,7 @@ - + @@ -14764,7 +14764,7 @@ - + @@ -14776,7 +14776,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -14796,7 +14796,7 @@ - + @@ -14805,7 +14805,7 @@ - + @@ -14814,7 +14814,7 @@ - + @@ -14823,12 +14823,12 @@ - + - + @@ -14853,7 +14853,7 @@ - + @@ -14862,7 +14862,7 @@ - + @@ -14871,7 +14871,7 @@ - + @@ -14879,7 +14879,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -14954,7 +14954,7 @@ - + @@ -14993,7 +14993,7 @@ - + @@ -15001,7 +15001,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -15390,7 +15390,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -15399,7 +15399,7 @@ - + @@ -15407,7 +15407,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -15431,7 +15431,7 @@ - + @@ -15439,7 +15439,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -15471,7 +15471,7 @@ - + @@ -15479,7 +15479,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -15517,9 +15517,9 @@ - - - + + + @@ -15583,7 +15583,7 @@ - + bold @@ -15594,7 +15594,7 @@ - + bold @@ -15602,7 +15602,7 @@ - + @@ -15610,7 +15610,7 @@ - + @@ -16662,7 +16662,7 @@ - + @@ -16671,7 +16671,7 @@ - + @@ -16680,7 +16680,7 @@ - + @@ -16689,7 +16689,7 @@ - + @@ -16709,7 +16709,7 @@ - + @@ -16730,7 +16730,7 @@ - + @@ -16739,7 +16739,7 @@ - + @@ -16760,7 +16760,7 @@ - + @@ -16782,7 +16782,7 @@ - + @@ -16804,7 +16804,7 @@ - + @@ -16825,7 +16825,7 @@ - + @@ -17085,7 +17085,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -17106,7 +17106,7 @@ - + @@ -17165,7 +17165,7 @@ - + @@ -17174,7 +17174,7 @@ - + @@ -17183,7 +17183,7 @@ - + @@ -17195,7 +17195,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -17330,7 +17330,7 @@ - + @@ -17339,7 +17339,7 @@ - + @@ -17347,7 +17347,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + @@ -17359,7 +17359,7 @@ - + @@ -17367,7 +17367,7 @@ KEEP_LOCATION KEEP_SIZE KEEP_RATIO - + diff --git a/tests/data/ContextDiagram.capella b/tests/data/ContextDiagram.capella index 9e3a267b..69d5d085 100644 --- a/tests/data/ContextDiagram.capella +++ b/tests/data/ContextDiagram.capella @@ -1,24 +1,24 @@ - + @@ -5202,7 +5202,7 @@ The predator is far away id="793e6da2-d019-4716-a5c5-af8ad550ca5e" name="Sub PC" kind="FIRMWARE" nature="NODE"> + id="38c69508-f12f-4e1d-a8c1-a422cbf8f358" name="Sub BUS"> @@ -5343,19 +5343,19 @@ The predator is far away id="b51ccc6f-5f96-4e28-b90e-72463a3b50cf" name="Network Switch" kind="HARDWARE" nature="NODE"> + id="53c9fe29-18e2-4642-906e-b7507bf0ff39" name="X NSwitch"> + id="f1c0db71-1927-4874-bf3e-0603a88f1d9b" name="X Switch"> + id="76d9c301-c0ad-4615-9f02-b804b018decf" name="X FSwitch"> @@ -5375,7 +5375,7 @@ The predator is far away id="5bfc516b-c20d-4007-9a38-5ba0e889d0a4" name="Camera Assembly" kind="HARDWARE" nature="NODE"> + id="2d1e2ad5-3439-4820-baa8-189966407ba2" name="X CA"> @@ -5386,7 +5386,7 @@ The predator is far away id="8a6c6ec9-095d-4d8b-9728-69bc79af5f27" name="Deploy Sub PC" kind="PERSON" nature="NODE"> + id="7400c2a3-913f-4a08-aca7-8dd7c389e5e9" name="Deploy BUS 1"> diff --git a/tests/test_capability_diagrams.py b/tests/test_capability_diagrams.py deleted file mode 100644 index 95d2d5ac..00000000 --- a/tests/test_capability_diagrams.py +++ /dev/null @@ -1,21 +0,0 @@ -# SPDX-FileCopyrightText: Copyright DB InfraGO AG and the capellambse-context-diagrams contributors -# SPDX-License-Identifier: Apache-2.0 - -import capellambse -import capellambse.metamodel as mm -import pytest - -# pylint: disable-next=relative-beyond-top-level, useless-suppression -from .conftest import SYSTEM_ANALYSIS_PARAMS # type: ignore[import-untyped] - -TEST_TYPES = (mm.oa.OperationalCapability, mm.sa.Capability, mm.sa.Mission) - - -@pytest.mark.parametrize("uuid", SYSTEM_ANALYSIS_PARAMS) -def test_context_diagrams(model: capellambse.MelodyModel, uuid: str) -> None: - obj = model.by_uuid(uuid) - assert isinstance(obj, TEST_TYPES), "Precondition failed" - - diag = obj.context_diagram - - assert diag.nodes diff --git a/tests/test_context_diagrams.py b/tests/test_context_diagrams.py index 08d643ed..e51e2003 100644 --- a/tests/test_context_diagrams.py +++ b/tests/test_context_diagrams.py @@ -4,8 +4,12 @@ import sys import capellambse +import capellambse.metamodel as mm import pytest +# pylint: disable-next=relative-beyond-top-level, useless-suppression +from .conftest import SYSTEM_ANALYSIS_PARAMS + TEST_CAP_SIZING_UUID = "b996a45f-2954-4fdd-9141-7934e7687de6" TEST_HUMAN_ACTOR_SIZING_UUID = "e95847ae-40bb-459e-8104-7209e86ea2d1" TEST_ACTOR_SIZING_UUID = "6c8f32bf-0316-477f-a23b-b5239624c28d" @@ -29,6 +33,20 @@ TEST_DERIVATION_UUID = "4ec45aec-0d6a-411a-80ee-ebd3c1a53d2c" TEST_PHYSICAL_PORT_UUID = "c403d4f4-9633-42a2-a5d6-9e1df2655146" +TEST_TYPES = (mm.oa.OperationalCapability, mm.sa.Capability, mm.sa.Mission) + + +@pytest.mark.parametrize("uuid", SYSTEM_ANALYSIS_PARAMS) +def test_capability_and_mission_context_diagrams( + model: capellambse.MelodyModel, uuid: str +) -> None: + obj = model.by_uuid(uuid) + assert isinstance(obj, TEST_TYPES), "Precondition failed" + + diag = obj.context_diagram + + assert diag.nodes + @pytest.mark.parametrize( "uuid", diff --git a/tests/test_diagram_view.py b/tests/test_diagram_view.py new file mode 100644 index 00000000..b20e83d0 --- /dev/null +++ b/tests/test_diagram_view.py @@ -0,0 +1,39 @@ +# SPDX-FileCopyrightText: Copyright DB InfraGO AG and the capellambse-context-diagrams contributors +# SPDX-License-Identifier: Apache-2.0 + +# SPDX-FileCopyrightText: 2022 Copyright DB InfraGO AG and the capellambse-context-diagrams contributors +# SPDX-License-Identifier: Apache-2.0 + +import capellambse +import capellambse.model as m +import pytest + + +@pytest.mark.parametrize( + "name", + [ + pytest.param( + "[SAB] Example Interface Context", id="Simple SAB diagram" + ), + pytest.param("[LAB] Hierarchy", id="Simple LAB diagram"), + pytest.param( + "[LAB] Hierarchy Function example", id="Nested LAB diagram" + ), + pytest.param( + "[PAB] Sub-component Cable Tree", id="Simple PAB diagram" + ), + pytest.param( + "[PAB] Example Physical Function Context Diagram", + id="Nested PAB diagram", + ), + ], +) +def test_capability_and_mission_context_diagrams( + model: capellambse.MelodyModel, name: str +) -> None: + obj = model.diagrams.by_name(name) + assert isinstance(obj, m.Diagram), "Precondition failed" + + diag = obj.auto_layout + + assert diag.nodes