From a0dc1f559ed8fe6004952d61e51c1732fef4a532 Mon Sep 17 00:00:00 2001 From: Guillaume Coutable Date: Fri, 19 Jun 2026 16:28:21 +0200 Subject: [PATCH] [2292] Fix list items inheritance of "Behavior" and "Step" elements Bug: https://github.com/eclipse-syson/syson/issues/2292 Signed-off-by: Guillaume Coutable --- CHANGELOG.adoc | 2 + ...=> GVCompartmentItemInheritanceTests.java} | 175 ++++++++++++++---- .../syson/tests/GeneralPurposeTests.java | 3 +- .../InheritedCompartmentItemFilterSwitch.java | 3 +- .../pages/release-notes/2026.7.0.adoc | 1 + 5 files changed, 147 insertions(+), 37 deletions(-) rename backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/{GVActionDefinitionParameterTests.java => GVCompartmentItemInheritanceTests.java} (54%) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index e87f0d53d..09efe2739 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -30,6 +30,8 @@ It has long been unused by Sirius Web itself (since the transition to MUI). - https://github.com/eclipse-syson/syson/issues/2232[#2232] [configuration] Fix a serialization problem of the View models of SysON representations. - https://github.com/eclipse-syson/syson/issues/2237[#2237] [diagrams] Fix the item label inside `frames`, `require constraints`, and `assume constraints` compartments. - https://github.com/eclipse-syson/syson/issues/2278[#2278] [diagrams] Display inherited behavior parameters in the _parameters_ compartment of `ActionDefinition` and `ActionUsage` graphical nodes. +- https://github.com/eclipse-syson/syson/issues/2292[#2292] [diagrams] Fix list items inheritance of "Behavior" and "Step" graphical elements. +It concerns, at least, _actions_ and _items_ compartments list items. === Improvements diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVActionDefinitionParameterTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVCompartmentItemInheritanceTests.java similarity index 54% rename from backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVActionDefinitionParameterTests.java rename to backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVCompartmentItemInheritanceTests.java index 17fafd83d..3f86f6cd3 100644 --- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVActionDefinitionParameterTests.java +++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/diagrams/general/view/GVCompartmentItemInheritanceTests.java @@ -22,6 +22,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; +import org.eclipse.emf.ecore.EClass; import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramEventInput; import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramRefreshedEventPayload; import org.eclipse.sirius.components.collaborative.diagrams.dto.ToolVariable; @@ -64,9 +65,9 @@ */ @Transactional @GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH }) -@SuppressWarnings("checkstyle:MultipleStringLiterals") +// CHECKSTYLE:OFF @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { SysONTestsProperties.NO_DEFAULT_LIBRARIES_PROPERTY }) -public class GVActionDefinitionParameterTests extends AbstractIntegrationTests { +public class GVCompartmentItemInheritanceTests extends AbstractIntegrationTests { private final IDescriptionNameGenerator descriptionNameGenerator = new SDVDescriptionNameGenerator(); @@ -150,9 +151,107 @@ public void checkActionDefinitionParameterFilter() { @DisplayName("GIVEN an ActionDefinition with behavior parameter, WHEN subclassing the ActionDefinition with another ActionDefinition, THEN the other ActionDefinition behavior parameters are inherited from the subclassed ActionDefinition") @Test - public void checkActionDefinitionParameterInheritance() { - String parameterToolName = "New Parameter In"; - String expectedListItemLabelText = "in ref parameter1"; + public void checkActionDefinitionParametersInheritanceWithSubclassification() { + this.checkListItemInBaseElementAreInheritedInSpecializingElement( + SysmlPackage.eINSTANCE.getActionDefinition(), + GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_DEFINITION_ID, + GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_DEFINITION_ID, + "New Parameter In", + "in ref parameter1", + true, + "parameters", + "New Action Definition", + SysmlPackage.eINSTANCE.getActionDefinition(), + "New Subclassification" + ); + } + + @DisplayName("GIVEN a base ActionUsage with behavior parameter, WHEN another ActionUsage is subsetting by reference the base ActionUsage, THEN the base ActionUsage behavior parameters are inherited by the other ActionUsage") + @Test + public void checkActionUsageParametersInheritanceWithReferenceSubsetting() { + this.checkListItemInBaseElementAreInheritedInSpecializingElement( + SysmlPackage.eINSTANCE.getActionUsage(), + GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_USAGE_ID, + GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_USAGE_ID, + "New Parameter Out", + "out ref parameter1", + true, + "parameters", + "New Action", + SysmlPackage.eINSTANCE.getActionUsage(), + "New Reference Subsetting" + ); + } + + @DisplayName("GIVEN an ActionDefinition with an action, WHEN using the ActionDefinition as a feature type of an ActionUsage, THEN the ActionUsage actions are inherited from the ActionDefinition") + @Test + public void checkActionDefinitionActionsInheritanceWithFeatureTyping() { + this.checkListItemInBaseElementAreInheritedInSpecializingElement( + SysmlPackage.eINSTANCE.getActionDefinition(), + GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_DEFINITION_ID, + GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_DEFINITION_ID, + "New Action", + "action1", + false, + "actions", + "New Action", + SysmlPackage.eINSTANCE.getActionUsage(), + "New Feature Typing" + ); + } + + @DisplayName("GIVEN a base ActionUsage with an action, WHEN subsetting the base ActionUsage with another ActionUsage, THEN the ActionUsage actions are inherited from the base ActionUsage") + @Test + public void checkActionUsageActionsInheritanceWithSubsetting() { + this.checkListItemInBaseElementAreInheritedInSpecializingElement( + SysmlPackage.eINSTANCE.getActionUsage(), + GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_USAGE_ID, + GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_USAGE_ID, + "New Action", + "action1", + false, + "actions", + "New Action", + SysmlPackage.eINSTANCE.getActionUsage(), + "New Subsetting" + ); + } + + @DisplayName("GIVEN a base ActionUsage with an item, WHEN subsetting the base ActionUsage with another ActionUsage, THEN the ActionUsage items are inherited from the base ActionUsage") + @Test + public void checkActionUsageItemsInheritanceWithSubsetting() { + this.checkListItemInBaseElementAreInheritedInSpecializingElement( + SysmlPackage.eINSTANCE.getActionUsage(), + GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_USAGE_ID, + GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_USAGE_ID, + "New Item", + "item1", + false, + "items", + "New Action", + SysmlPackage.eINSTANCE.getActionUsage(), + "New Subsetting" + ); + } + + @DisplayName("GIVEN an ActionUsage with an action, WHEN a StateUsage is subsetting by reference the ActionUsage, THEN the ActionUsage actions are inherited by the StateUsage") + @Test + public void checkActionUsageActionsInheritanceWithReferenceSubsetting() { + this.checkListItemInBaseElementAreInheritedInSpecializingElement( + SysmlPackage.eINSTANCE.getActionUsage(), + GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_USAGE_ID, + GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_USAGE_ID, + "New Action", + "action1", + false, + "actions", + "New State", + SysmlPackage.eINSTANCE.getStateUsage(), + "New Reference Subsetting" + ); + } + + private void checkListItemInBaseElementAreInheritedInSpecializingElement(EClass baseElementToInheritFrom, String baseElementToInheritFromTargetObjectId, String baseElementToInheritFromNodeId, String elementToInheritCreationToolName, String elementToInheritExpectedListItemLabelText, boolean alsoRepresentedByBorderNode, String compartmentName, String elementThatInheritFromBaseElementCreationToolName, EClass elementThatInheritFromBaseElementEClass, String specializationToolName) { var flux = this.givenSubscriptionToDiagram(); var diagramDescription = this.givenDiagramDescription.getDiagramDescription(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID); @@ -161,70 +260,76 @@ public void checkActionDefinitionParameterInheritance() { AtomicReference diagram = new AtomicReference<>(); Consumer initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram::set); - // Create parameter on ActionDefinition - String createParameterToolId = diagramDescriptionIdProvider.getNodeToolId(this.descriptionNameGenerator.getNodeName(SysmlPackage.eINSTANCE.getActionDefinition()), parameterToolName); - Runnable newCreationTool = () -> this.toolTester.invokeTool(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, diagram, GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_DEFINITION_ID, createParameterToolId); - Consumer createdParameterDiagramConsumer = assertRefreshedDiagramThat(newDiagram -> { + // Create an element in the base element to inherit + String createActionToolId = diagramDescriptionIdProvider.getNodeToolId(this.descriptionNameGenerator.getNodeName(baseElementToInheritFrom), elementToInheritCreationToolName); + Runnable newCreationTool = () -> this.toolTester.invokeTool(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, diagram, baseElementToInheritFromTargetObjectId, createActionToolId); + Consumer createdActionDiagramConsumer = assertRefreshedDiagramThat(newDiagram -> { + int expectedBorderNodeCount = 0; + if (alsoRepresentedByBorderNode) { + expectedBorderNodeCount = 1; + } new CheckDiagramElementCount(this.diagramComparator) - .hasNewBorderNodeCount(1) - .hasNewNodeCount(1) // The parameter is created + .hasNewBorderNodeCount(expectedBorderNodeCount) // The element is created and sometimes can be represented as a border node + .hasNewNodeCount(1) // The element is created and represented as a list item .hasNewEdgeCount(0) .check(diagram.get(), newDiagram, true); - var parameterCompartment = new DiagramNavigator(newDiagram) - .nodeWithTargetObjectId(GeneralViewWithTopNodesTestProjectData.SemanticIds.ACTION_DEFINITION_ID) - .childNodeWithLabel("parameters") + var actionsCompartment = new DiagramNavigator(newDiagram) + .nodeWithTargetObjectId(baseElementToInheritFromTargetObjectId) + .childNodeWithLabel(compartmentName) .getNode(); - assertThat(parameterCompartment.getChildNodes()).hasSize(1); - assertThat(parameterCompartment.getChildNodes().getFirst().getInsideLabel().getText()).isEqualTo(expectedListItemLabelText); + assertThat(actionsCompartment.getChildNodes()).hasSize(1); + assertThat(actionsCompartment.getChildNodes().getFirst().getInsideLabel().getText()).isEqualTo(elementToInheritExpectedListItemLabelText); // The created element has the expected name diagram.set(newDiagram); }); - AtomicReference newActionDefinitionNodeId = new AtomicReference<>(); - // Create another ActionDefinition - String createActionDefinitionToolId = diagramDescriptionIdProvider.getDiagramCreationToolId("New Action Definition"); - Runnable createActionDefinitionRunnable = () -> this.toolTester.invokeTool(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, diagram, null, createActionDefinitionToolId); + // Create an new element that will inherit from the base element + AtomicReference newActionActionUsageNodeId = new AtomicReference<>(); + String createActionUsageToolId = diagramDescriptionIdProvider.getDiagramCreationToolId(elementThatInheritFromBaseElementCreationToolName); + Runnable createActionDefinitionRunnable = () -> this.toolTester.invokeTool(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, diagram, null, createActionUsageToolId); Consumer createdActionDefinitionDiagramConsumer = assertRefreshedDiagramThat(newDiagram -> { new CheckDiagramElementCount(this.diagramComparator) .hasNewBorderNodeCount(0) - .hasNewNodeCount(1) // The Action definition + .hasNewNodeCount(1) // The new element is created .hasNewEdgeCount(0) .check(diagram.get(), newDiagram, true); var newNodes = this.diagramComparator.newNodes(diagram.get(), newDiagram); - newActionDefinitionNodeId.set(newNodes.getFirst().getId()); + newActionActionUsageNodeId.set(newNodes.getFirst().getId()); diagram.set(newDiagram); }); - // Create the subclassification between the created ActionDefinition and the existing one - String createSubclassificationToolId = diagramDescriptionIdProvider.getEdgeCreationToolId(this.descriptionNameGenerator.getNodeName(SysmlPackage.eINSTANCE.getActionDefinition()), "New Subclassification"); - Runnable createSubclassification = () -> this.edgeCreationTester.createEdgeUsingNodeId(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, diagram, newActionDefinitionNodeId.get(), GeneralViewWithTopNodesTestProjectData.GraphicalIds.ACTION_DEFINITION_ID, createSubclassificationToolId); - // check the parameter is inherited and contains a '^' in its name - Consumer createSubclassificationDiagramConsumer = assertRefreshedDiagramThat(newDiagram -> { + // Create the specialization between the newly created element and the base element + String createFeatureTypeToolId = diagramDescriptionIdProvider.getEdgeCreationToolId(this.descriptionNameGenerator.getNodeName(elementThatInheritFromBaseElementEClass), specializationToolName); + Runnable createFeatureTyping = () -> this.edgeCreationTester.createEdgeUsingNodeId(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, diagram, newActionActionUsageNodeId.get(), baseElementToInheritFromNodeId, createFeatureTypeToolId); + + // Check new created element inherits from the base element, and thus, contains a list item with '^' in its label + Consumer createFeatureTypeDiagramConsumer = assertRefreshedDiagramThat(newDiagram -> { new CheckDiagramElementCount(this.diagramComparator) .hasNewBorderNodeCount(0) - .hasNewNodeCount(1) // The list item inherited from subclassed element - .hasNewEdgeCount(1) // The subclassification edge + .hasNewNodeCount(1) // The list item inherited from the base element + .hasNewEdgeCount(1) // The specialization edge .check(diagram.get(), newDiagram); var parameterCompartment = new DiagramNavigator(newDiagram) - .nodeWithId(newActionDefinitionNodeId.get()) - .childNodeWithLabel("parameters") + .nodeWithId(newActionActionUsageNodeId.get()) + .childNodeWithLabel(compartmentName) .getNode(); assertThat(parameterCompartment.getChildNodes()).hasSize(1); - assertThat(parameterCompartment.getChildNodes().getFirst().getInsideLabel().getText()).isEqualTo("^" + expectedListItemLabelText); + assertThat(parameterCompartment.getChildNodes().getFirst().getInsideLabel().getText()).isEqualTo("^" + elementToInheritExpectedListItemLabelText); // The inheriting element has the same name prefixed with '^' }); StepVerifier.create(flux) .consumeNextWith(initialDiagramContentConsumer) .then(newCreationTool) - .consumeNextWith(createdParameterDiagramConsumer) + .consumeNextWith(createdActionDiagramConsumer) .then(createActionDefinitionRunnable) .consumeNextWith(createdActionDefinitionDiagramConsumer) - .then(createSubclassification) - .consumeNextWith(createSubclassificationDiagramConsumer) + .then(createFeatureTyping) + .consumeNextWith(createFeatureTypeDiagramConsumer) .thenCancel() .verify(Duration.ofSeconds(10)); } } +// CHECKSTYLE:ON diff --git a/backend/tests/syson-tests/src/test/java/org/eclipse/syson/tests/GeneralPurposeTests.java b/backend/tests/syson-tests/src/test/java/org/eclipse/syson/tests/GeneralPurposeTests.java index 47fc70377..86751463d 100644 --- a/backend/tests/syson-tests/src/test/java/org/eclipse/syson/tests/GeneralPurposeTests.java +++ b/backend/tests/syson-tests/src/test/java/org/eclipse/syson/tests/GeneralPurposeTests.java @@ -237,7 +237,8 @@ private void testNoSuppressWarnings(int index, String line, Path javaFilePath, L private void testNoCheckstyleOff(int index, String line, Path javaFilePath) { var whitelist = Stream.of( Path.of("ServiceMethod.java"), - Path.of("GVTopNodeCreationTests.java") + Path.of("GVTopNodeCreationTests.java"), + Path.of("GVCompartmentItemInheritanceTests.java") ); if (whitelist.filter(javaFilePath::endsWith).findFirst().isEmpty()) { if (line.contains(CHECKSTYLE_OFF)) { diff --git a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/InheritedCompartmentItemFilterSwitch.java b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/InheritedCompartmentItemFilterSwitch.java index 523023a1f..e539e5dd5 100644 --- a/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/InheritedCompartmentItemFilterSwitch.java +++ b/backend/views/syson-diagram-common-view/src/main/java/org/eclipse/syson/diagram/common/view/services/InheritedCompartmentItemFilterSwitch.java @@ -109,7 +109,8 @@ public Boolean caseReferenceUsage(ReferenceUsage object) { private boolean shouldConsiderParameter(Feature feature) { Type owningType = feature.getOwningType(); - return owningType instanceof Behavior || owningType instanceof Step; + return (owningType instanceof Behavior && this.eReference.equals(SysmlPackage.eINSTANCE.getBehavior_Parameter())) + || (owningType instanceof Step && this.eReference.equals(SysmlPackage.eINSTANCE.getStep_Parameter())); } private boolean isInheritedParameter(Feature feature) { diff --git a/doc/content/modules/user-manual/pages/release-notes/2026.7.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2026.7.0.adoc index 52f96f0fa..9f4fc3887 100644 --- a/doc/content/modules/user-manual/pages/release-notes/2026.7.0.adoc +++ b/doc/content/modules/user-manual/pages/release-notes/2026.7.0.adoc @@ -114,6 +114,7 @@ image::expression-palette-section.png[Expression palette tool section on an `Att + image::action-flow-guard-labels.png[Succession edges with visible guard expressions, width=80%] ** Display inherited behavior parameters in the _parameters_ compartment of `ActionDefinition` and `ActionUsage` graphical nodes. +** Display inherited items in the _items_ compartment, and inherited actions in the _actions_ compartment of `Behavior` and `Steps` graphical nodes. == Technical details