Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ This is currently supported on `Features` (e.g. `Attribute`), `Constraints` and
It leverages the selection dialog to either create an _occurrence timeslice/snapshot_, or the _usage timeslice/snapshot_ matching the `OccurrenceDefinition` on which the tool is applied.
- https://github.com/eclipse-syson/syson/issues/2250[#2250] [diagrams] Leverage the selection dialog to improve the graphical node tools creating a _require_ `ConstraintUsage`, or an _assume_ `ConstraintUsage`, from `RequirementUsage` and `RequirementDefinition` graphical nodes.
- https://github.com/eclipse-syson/syson/issues/2254[#2254] [diagrams] Add the support for _assume_ and _require_ graphical edges.
- https://github.com/eclipse-syson/syson/issues/2260[#2260] [diagrams] Add the _New Assume Constraint_ or _New Require Constraint_ edge tools to create _assume_ and _require_ graphical edges.

== v2026.5.0

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/*******************************************************************************
* Copyright (c) 2026 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.syson.application.controllers.diagrams.general.view;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.InstanceOfAssertFactories.type;
import static org.eclipse.sirius.components.diagrams.tests.DiagramEventPayloadConsumer.assertRefreshedDiagramThat;
import static org.eclipse.sirius.components.diagrams.tests.assertions.DiagramInstanceOfAssertFactories.EDGE;
import static org.eclipse.sirius.components.diagrams.tests.assertions.DiagramInstanceOfAssertFactories.EDGE_STYLE;

import java.time.Duration;
import java.util.List;
import java.util.UUID;
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.core.api.IObjectSearchService;
import org.eclipse.sirius.components.diagrams.ArrowStyle;
import org.eclipse.sirius.components.diagrams.Diagram;
import org.eclipse.sirius.components.diagrams.Edge;
import org.eclipse.sirius.components.diagrams.Label;
import org.eclipse.sirius.components.diagrams.ViewModifier;
import org.eclipse.sirius.components.view.emf.diagram.IDiagramIdProvider;
import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState;
import org.eclipse.syson.AbstractIntegrationTests;
import org.eclipse.syson.GivenSysONServer;
import org.eclipse.syson.application.controller.editingcontext.checkers.SemanticCheckerService;
import org.eclipse.syson.application.controllers.diagrams.testers.EdgeCreationTester;
import org.eclipse.syson.application.data.GeneralViewWithTopNodesTestProjectData;
import org.eclipse.syson.services.SemanticRunnableFactory;
import org.eclipse.syson.services.diagrams.DiagramComparator;
import org.eclipse.syson.services.diagrams.DiagramDescriptionIdProvider;
import org.eclipse.syson.services.diagrams.api.IGivenDiagramDescription;
import org.eclipse.syson.services.diagrams.api.IGivenDiagramSubscription;
import org.eclipse.syson.standard.diagrams.view.SDVDescriptionNameGenerator;
import org.eclipse.syson.sysml.RequirementConstraintMembership;
import org.eclipse.syson.sysml.SysmlPackage;
import org.eclipse.syson.sysml.metamodel.helper.LabelConstants;
import org.eclipse.syson.util.IDescriptionNameGenerator;
import org.eclipse.syson.util.SysONRepresentationDescriptionIdentifiers;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.transaction.annotation.Transactional;

import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;

/**
* {@link org.eclipse.syson.sysml.RequirementConstraintMembership} related test in General View.
*
* @author gcoutable
*/
@Transactional
@GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH })
@SuppressWarnings("checkstyle:MultipleStringLiterals")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class GVRequirementConstraintMembershipTests extends AbstractIntegrationTests {

@Autowired
private IGivenInitialServerState givenInitialServerState;

@Autowired
private IGivenDiagramSubscription givenDiagramSubscription;

@Autowired
private IGivenDiagramDescription givenDiagramDescription;

@Autowired
private IDiagramIdProvider diagramIdProvider;

@Autowired
private DiagramComparator diagramComparator;

@Autowired
private SemanticRunnableFactory semanticRunnableFactory;

@Autowired
private IObjectSearchService objectSearchService;

@Autowired
private EdgeCreationTester edgeCreationTester;

private final IDescriptionNameGenerator descriptionNameGenerator = new SDVDescriptionNameGenerator();

private SemanticCheckerService semanticCheckerService;

private Flux<DiagramRefreshedEventPayload> givenSubscriptionToDiagram() {
var diagramEventInput = new DiagramEventInput(UUID.randomUUID(), GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, GeneralViewWithTopNodesTestProjectData.GraphicalIds.DIAGRAM_ID);
return this.givenDiagramSubscription.subscribe(diagramEventInput);
}

@BeforeEach
public void setUp() {
this.givenInitialServerState.initialize();
this.semanticCheckerService = new SemanticCheckerService(this.semanticRunnableFactory, this.objectSearchService, GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID,
GeneralViewWithTopNodesTestProjectData.SemanticIds.PACKAGE_1_ID);
}

@DisplayName("GIVEN the general view diagram, WHEN the assume edge tool between a RequirementUsage graphical node and a ConstraintUsage graphical node is used, THEN it creates an assume edge between both elements")
@Test
public void testCreateAssumeConstraintWithEdgeToolBetweenRequirementUsageAndConstraintUsage() {
this.createRequirementConstraintMembershipWithEdgeTool(SysmlPackage.eINSTANCE.getRequirementUsage(), "New Assume Constraint", GeneralViewWithTopNodesTestProjectData.GraphicalIds.REQUIREMENT_USAGE_ID, LabelConstants.OPEN_QUOTE + LabelConstants.ASSUME + LabelConstants.CLOSE_QUOTE, "requirement");
}

@DisplayName("GIVEN the general view diagram, WHEN the assume edge tool between a RequirementDefinition graphical node and a ConstraintUsage graphical node is used, THEN it creates an assume edge between both elements")
@Test
public void testCreateAssumeConstraintWithEdgeToolBetweenRequirementDefinitionAndConstraintUsage() {
this.createRequirementConstraintMembershipWithEdgeTool(SysmlPackage.eINSTANCE.getRequirementDefinition(), "New Assume Constraint", GeneralViewWithTopNodesTestProjectData.GraphicalIds.REQUIREMENT_DEFINITION_ID, LabelConstants.OPEN_QUOTE + LabelConstants.ASSUME + LabelConstants.CLOSE_QUOTE, "RequirementDefinition");
}

@DisplayName("GIVEN the general view diagram, WHEN the require edge tool between a RequirementUsage graphical node and a ConstraintUsage graphical node is used, THEN it creates a require edge between both elements")
@Test
public void testCreateRequireConstraintWithEdgeToolBetweenRequirementUsageAndConstraintUsage() {
this.createRequirementConstraintMembershipWithEdgeTool(SysmlPackage.eINSTANCE.getRequirementUsage(), "New Require Constraint", GeneralViewWithTopNodesTestProjectData.GraphicalIds.REQUIREMENT_USAGE_ID, LabelConstants.OPEN_QUOTE + LabelConstants.REQUIRE + LabelConstants.CLOSE_QUOTE, "requirement");
}

@DisplayName("GIVEN the general view diagram, WHEN the require edge tool between a RequirementDefinition graphical node and a ConstraintUsage graphical node is used, THEN it creates a require edge between both elements")
@Test
public void testCreateRequireConstraintWithEdgeToolBetweenRequirementDefinitionAndConstraintUsage() {
this.createRequirementConstraintMembershipWithEdgeTool(SysmlPackage.eINSTANCE.getRequirementDefinition(), "New Require Constraint", GeneralViewWithTopNodesTestProjectData.GraphicalIds.REQUIREMENT_DEFINITION_ID, LabelConstants.OPEN_QUOTE + LabelConstants.REQUIRE + LabelConstants.CLOSE_QUOTE, "RequirementDefinition");
}

private void createRequirementConstraintMembershipWithEdgeTool(EClass parentEClass, String toolName, String graphicalSourceNodeId, String expectedEdgeLabel, String parentLabel) {
var flux = this.givenSubscriptionToDiagram();

var diagramDescription = this.givenDiagramDescription.getDiagramDescription(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID,
SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID);
var diagramDescriptionIdProvider = new DiagramDescriptionIdProvider(diagramDescription, this.diagramIdProvider);

var edgeCreationToolId = diagramDescriptionIdProvider.getEdgeCreationToolId(
this.descriptionNameGenerator.getNodeName(parentEClass),
toolName);

AtomicReference<Diagram> diagram = new AtomicReference<>();
Consumer<Object> initialDiagramContentConsumer = assertRefreshedDiagramThat(diagram::set);

Runnable createEdgeRunnable = () -> this.edgeCreationTester.createEdgeUsingNodeId(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID,
diagram,
graphicalSourceNodeId,
GeneralViewWithTopNodesTestProjectData.GraphicalIds.CONSTRAINT_USAGE_ID,
edgeCreationToolId);

Consumer<Object> diagramContentConsumerAfterNewEdge = assertRefreshedDiagramThat(newDiagram -> {
var newEdges = this.diagramComparator.newEdges(diagram.get(), newDiagram);
var newVisibleEdges = newEdges.stream().filter(edge -> edge.getState() != ViewModifier.Hidden).toList();

assertThat(newVisibleEdges).hasSize(1).first(EDGE)
.hasSourceId(graphicalSourceNodeId)
.hasTargetId(GeneralViewWithTopNodesTestProjectData.GraphicalIds.CONSTRAINT_USAGE_ID)
.extracting(Edge::getCenterLabel)
.extracting(Label::text)
.hasToString(expectedEdgeLabel);

assertThat(newVisibleEdges).hasSize(1).first(EDGE)
.extracting(Edge::getStyle, EDGE_STYLE)
.hasSourceArrow(ArrowStyle.None)
.hasTargetArrow(ArrowStyle.InputArrow);
});

Consumer<Object> additionalCheck = object -> {
assertThat(object).isInstanceOf(List.class)
.asInstanceOf(type(List.class))
.satisfies(requirementConstraintMemberships -> {
assertThat((List<?>) requirementConstraintMemberships).size().isEqualTo(1);
assertThat(requirementConstraintMemberships.getFirst())
.isInstanceOf(RequirementConstraintMembership.class)
.asInstanceOf(type(RequirementConstraintMembership.class))
.satisfies(requirementConstraintMembership -> {
var ownedConstraint = requirementConstraintMembership.getOwnedConstraint();
var ownedSubsetting = ownedConstraint.getOwnedSubsetting();
assertThat(ownedSubsetting).isNotEmpty();
assertThat(ownedSubsetting.getFirst().getSubsettedFeature()).isEqualTo(requirementConstraintMembership.getReferencedConstraint());
});
});
};

Runnable semanticCheck = this.semanticCheckerService.checkEditingContext(this.semanticCheckerService.getElementInParentSemanticChecker(parentLabel, SysmlPackage.eINSTANCE.getElement_OwnedRelationship(), SysmlPackage.eINSTANCE.getRequirementConstraintMembership(), additionalCheck));

StepVerifier.create(flux)
.consumeNextWith(initialDiagramContentConsumer)
.then(createEdgeRunnable)
.consumeNextWith(diagramContentConsumerAfterNewEdge)
.then(semanticCheck)
.thenCancel()
.verify(Duration.ofSeconds(10));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public static class GraphicalIds {

public static final String CONCERN_USAGE_ID = "0999b8c3-d37c-3644-a1d6-b9777a499d11";

public static final String CONSTRAINT_USAGE_ID = "22da3b61-32f6-346e-9deb-7dd1f64bfd69";

public static final String DIAGRAM_ID = "fa8c8a8d-2813-404c-876f-c04e8b297134";

public static final String ITEM_DEFINITION_ID = "df3542d9-6314-3da5-993c-a296f4042674";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.eclipse.syson.diagram.services.aql.DiagramMutationAQLService;
import org.eclipse.syson.diagram.services.aql.DiagramQueryAQLService;
import org.eclipse.syson.model.services.aql.ModelMutationAQLService;
import org.eclipse.syson.sysml.RequirementConstraintKind;
import org.eclipse.syson.sysml.SysmlPackage;
import org.eclipse.syson.sysml.Usage;
import org.eclipse.syson.util.AQLConstants;
Expand Down Expand Up @@ -682,4 +683,56 @@ public EdgeTool createFramedConcernEdgeTool() {
.targetElementDescriptions(targetNodeDescriptions.toArray(NodeDescription[]::new))
.build();
}

public EdgeTool createAssumeConstraintEdgeTool() {
var builder = this.diagramBuilderHelper.newEdgeTool();
var body = this.viewBuilderHelper.newChangeContext()
.expression(ServiceMethod.of2(ModelMutationAQLService::createConstraint).aql(EdgeDescription.SEMANTIC_EDGE_SOURCE, EdgeDescription.SEMANTIC_EDGE_TARGET, "assumptionConstraint"));

var targetNodeDescriptions = new ArrayList<NodeDescription>();
var constraintUsageNodeDescriptionName = this.nameGenerator.getNodeName(SysmlPackage.eINSTANCE.getConstraintUsage());
this.allNodeDescriptions.stream()
.filter(nd -> constraintUsageNodeDescriptionName.equals(nd.getName()))
.findFirst()
.ifPresent(targetNodeDescriptions::add);

String requirementConstraintKindType = SysMLMetamodelHelper.buildQualifiedName(SysmlPackage.eINSTANCE.getRequirementConstraintKind());
var letRequirementConstraintLiteral = this.viewBuilderHelper.newLet()
.variableName("assumptionConstraint")
.valueExpression(AQLConstants.AQL + requirementConstraintKindType + ".getEEnumLiteral('" + RequirementConstraintKind.ASSUMPTION.getLiteral() + "').instance")
.children(body.build());

return builder
.name(this.nameGenerator.getCreationToolName("New Assume ", SysmlPackage.eINSTANCE.getConstraintUsage()))
.iconURLsExpression(METAMODEL_ICONS_PATH + SysmlPackage.eINSTANCE.getRequirementConstraintMembership().getName() + SVG)
.body(letRequirementConstraintLiteral.build())
.targetElementDescriptions(targetNodeDescriptions.toArray(NodeDescription[]::new))
.build();
}

public EdgeTool createRequireConstraintEdgeTool() {
var builder = this.diagramBuilderHelper.newEdgeTool();
var body = this.viewBuilderHelper.newChangeContext()
.expression(ServiceMethod.of2(ModelMutationAQLService::createConstraint).aql(EdgeDescription.SEMANTIC_EDGE_SOURCE, EdgeDescription.SEMANTIC_EDGE_TARGET, "requirementConstraint"));

var targetNodeDescriptions = new ArrayList<NodeDescription>();
var constraintUsageNodeDescriptionName = this.nameGenerator.getNodeName(SysmlPackage.eINSTANCE.getConstraintUsage());
this.allNodeDescriptions.stream()
.filter(nd -> constraintUsageNodeDescriptionName.equals(nd.getName()))
.findFirst()
.ifPresent(targetNodeDescriptions::add);

String requirementConstraintKindType = SysMLMetamodelHelper.buildQualifiedName(SysmlPackage.eINSTANCE.getRequirementConstraintKind());
var letRequirementConstraintLiteral = this.viewBuilderHelper.newLet()
.variableName("requirementConstraint")
.valueExpression(AQLConstants.AQL + requirementConstraintKindType + ".getEEnumLiteral('" + RequirementConstraintKind.REQUIREMENT.getLiteral() + "').instance")
.children(body.build());

return builder
.name(this.nameGenerator.getCreationToolName("New Require ", SysmlPackage.eINSTANCE.getConstraintUsage()))
.iconURLsExpression(METAMODEL_ICONS_PATH + SysmlPackage.eINSTANCE.getRequirementConstraintMembership().getName() + SVG)
.body(letRequirementConstraintLiteral.build())
.targetElementDescriptions(targetNodeDescriptions.toArray(NodeDescription[]::new))
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ public List<EdgeTool> casePortUsage(PortUsage object) {
public List<EdgeTool> caseRequirementDefinition(RequirementDefinition object) {
var edgeTools = new ArrayList<EdgeTool>();
edgeTools.add(this.edgeToolService.createFramedConcernEdgeTool());
edgeTools.add(this.edgeToolService.createAssumeConstraintEdgeTool());
edgeTools.add(this.edgeToolService.createRequireConstraintEdgeTool());
edgeTools.addAll(this.caseDefinition(object));
return edgeTools;
}
Expand All @@ -277,6 +279,8 @@ public List<EdgeTool> caseRequirementUsage(RequirementUsage object) {
.toList();
edgeTools.add(this.edgeToolService.createBecomeObjectiveRequirementEdgeTool(objectiveTargets));
edgeTools.add(this.edgeToolService.createFramedConcernEdgeTool());
edgeTools.add(this.edgeToolService.createAssumeConstraintEdgeTool());
edgeTools.add(this.edgeToolService.createRequireConstraintEdgeTool());
edgeTools.addAll(this.caseUsage(object));
return edgeTools;
}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,15 @@ image::release-notes-frames-compartment.png[frames compartment displaying concer
It leverages the selection dialog to either create an _occurrence timeslice/snapshot_, or the _usage timeslice/snapshot_ matching the `OccurrenceDefinition` on which the tool is applied.

** Add the support for _assume_ and _require_ graphical edges.
These edges are displayed using the tool to create a _require_ `ConstraintUsage`, or an _assume_ `ConstraintUsage`, from `RequirementUsage` and `RequirementDefinition` graphical nodes, when another `ConstraintUsage` is selected.
These graphical edges are displayed using the tool to create a _require_ `ConstraintUsage`, or an _assume_ `ConstraintUsage`, from `RequirementUsage` and `RequirementDefinition` graphical nodes, when another `ConstraintUsage` is selected.
These graphical edges can also be created using the _New Assume Constraint_ or _New Require Constraint_ graphical edge tools.
+
image::release-notes-assume-and-require-edges.png[assume and require edge between a RequirementDefinition and two different ConstraintUsage, width=60%,height=60%]

** Add the _New Assume Constraint_ or _New Require Constraint_ edge tools to create _assume_ and _require_ graphical edges.
+
image::release-notes-assume-and-require-edge-tools.png[list available edge tools containing the two new tools, between a RequirementUsage and a ConstraintUsage, width=60%,height=60%]

* In the _Explorer_ view:

** The tree items corresponding to the internals of `Expression` elements (syntax tree) are now hidden by default.
Expand Down
Loading