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 @@ -29,6 +29,7 @@

=== New features

- https://github.com/eclipse-syson/syson/issues/1396[#1396] [general-view] Add a graphical edge representation for `IncludeUseCaseUsage` in the _General View_ diagram.

== v2025.6.0

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
/*******************************************************************************
* Copyright (c) 2025 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.eclipse.sirius.components.diagrams.tests.assertions.DiagramAssertions.assertThat;

import java.time.Duration;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;

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.IIdentityService;
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.events.ReconnectEdgeKind;
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.SysONTestsProperties;
import org.eclipse.syson.application.controller.editingContext.checkers.SemanticCheckerService;
import org.eclipse.syson.application.controllers.diagrams.checkers.CheckDiagramElementCount;
import org.eclipse.syson.application.controllers.diagrams.checkers.DiagramCheckerService;
import org.eclipse.syson.application.controllers.diagrams.checkers.IDiagramChecker;
import org.eclipse.syson.application.controllers.diagrams.testers.EdgeCreationTester;
import org.eclipse.syson.application.controllers.diagrams.testers.EdgeReconnectionTester;
import org.eclipse.syson.application.data.IncludeUseCaseUsageProjectData;
import org.eclipse.syson.diagram.general.view.GVDescriptionNameGenerator;
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.IGivenDiagramReference;
import org.eclipse.syson.services.diagrams.api.IGivenDiagramSubscription;
import org.eclipse.syson.sysml.IncludeUseCaseUsage;
import org.eclipse.syson.sysml.SysmlPackage;
import org.eclipse.syson.sysml.helper.LabelConstants;
import org.eclipse.syson.util.IDescriptionNameGenerator;
import org.eclipse.syson.util.SysONRepresentationDescriptionIdentifiers;
import org.junit.jupiter.api.AfterEach;
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.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.transaction.annotation.Transactional;

import reactor.test.StepVerifier;

/**
* Tests on {@link IncludeUseCaseUsage} on the General View Diagram .
*
* @author Arthur Daussy
*/
@Transactional
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { SysONTestsProperties.NO_DEFAULT_LIBRARIES_PROPERTY })
public class GVIncludeUseCaseUsageTests extends AbstractIntegrationTests {

private final IDescriptionNameGenerator descriptionNameGenerator = new GVDescriptionNameGenerator();

@Autowired
private IGivenInitialServerState givenInitialServerState;

@Autowired
private IGivenDiagramReference givenDiagram;

@Autowired
private IGivenDiagramDescription givenDiagramDescription;

@Autowired
private IGivenDiagramSubscription givenDiagramSubscription;

@Autowired
private IDiagramIdProvider diagramIdProvider;

@Autowired
private EdgeCreationTester edgeCreationTester;

@Autowired
private DiagramComparator diagramComparator;

@Autowired
private IObjectSearchService objectSearchService;

@Autowired
private SemanticRunnableFactory semanticRunnableFactory;

@Autowired
private EdgeReconnectionTester edgeReconnectionTester;

@Autowired
private IIdentityService identityService;

private DiagramDescriptionIdProvider diagramDescriptionIdProvider;

private DiagramCheckerService diagramCheckerService;

private StepVerifier.Step<DiagramRefreshedEventPayload> verifier;

private SemanticCheckerService semanticCheckerService;

private AtomicReference<Diagram> diagram;

@BeforeEach
public void setUp() {
this.givenInitialServerState.initialize();
var diagramEventInput = new DiagramEventInput(UUID.randomUUID(),
IncludeUseCaseUsageProjectData.EDITING_CONTEXT_ID,
IncludeUseCaseUsageProjectData.GraphicalIds.DIAGRAM_ID);
var flux = this.givenDiagramSubscription.subscribe(diagramEventInput);
this.verifier = StepVerifier.create(flux);
this.diagram = this.givenDiagram.getDiagram(this.verifier);
var diagramDescription = this.givenDiagramDescription.getDiagramDescription(IncludeUseCaseUsageProjectData.EDITING_CONTEXT_ID,
SysONRepresentationDescriptionIdentifiers.GENERAL_VIEW_DIAGRAM_DESCRIPTION_ID);
this.diagramDescriptionIdProvider = new DiagramDescriptionIdProvider(diagramDescription, this.diagramIdProvider);
this.diagramCheckerService = new DiagramCheckerService(this.diagramComparator, this.descriptionNameGenerator);
this.semanticCheckerService = new SemanticCheckerService(this.semanticRunnableFactory, this.objectSearchService, IncludeUseCaseUsageProjectData.EDITING_CONTEXT_ID,
IncludeUseCaseUsageProjectData.SemanticIds.PACKAGE_1_ID);
}

@AfterEach
public void tearDown() {
if (this.verifier != null) {
this.verifier.thenCancel()
.verify(Duration.ofSeconds(10));
}
}

@DisplayName("GIVEN two UseCaseUsages, WHEN creating an IncludeUseCaseUsage between them, THEN an edge should be displayed to represent that new element as an edge")
@Sql(scripts = { IncludeUseCaseUsageProjectData.SCRIPT_PATH }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED))
@Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED))
@Test
public void checkIncludeUsageActionCreation() {
String creationToolId = this.diagramDescriptionIdProvider.getEdgeCreationToolId(this.descriptionNameGenerator.getNodeName(SysmlPackage.eINSTANCE.getUseCaseUsage()),
"New Include Use Case");
this.verifier.then(() -> this.edgeCreationTester.createEdgeUsingNodeId(IncludeUseCaseUsageProjectData.EDITING_CONTEXT_ID,
this.diagram,
IncludeUseCaseUsageProjectData.GraphicalIds.INCLUDING_USE_CASE_ID,
IncludeUseCaseUsageProjectData.GraphicalIds.INCLUDED_USE_CASE_2_ID,
creationToolId));

String[] newInclude = new String[1];
IDiagramChecker diagramChecker = (initialDiagram, newDiagram) -> {
new CheckDiagramElementCount(this.diagramComparator)
.hasNewNodeCount(1) // New element in the Perform action compartment
.hasNewEdgeCount(1)
.check(initialDiagram, newDiagram);
Edge newEdge = this.diagramComparator.newEdges(initialDiagram, newDiagram).get(0);
newInclude[0] = newEdge.getTargetObjectId();
assertThat(newEdge).hasSourceId(IncludeUseCaseUsageProjectData.GraphicalIds.INCLUDING_USE_CASE_ID);
assertThat(newEdge).hasTargetId(IncludeUseCaseUsageProjectData.GraphicalIds.INCLUDED_USE_CASE_2_ID);
assertThat(newEdge.getStyle()).hasTargetArrow(ArrowStyle.InputFillClosedArrow);
assertThat(newEdge.getCenterLabel().getText()).isEqualTo(LabelConstants.OPEN_QUOTE + "include" + LabelConstants.CLOSE_QUOTE);
};

this.diagramCheckerService.checkDiagram(diagramChecker, this.diagram, this.verifier);

this.semanticCheckerService.checkElement(this.verifier, IncludeUseCaseUsage.class, () -> newInclude[0], includeUsage -> {
assertThat(this.identityService.getId(includeUsage.getUseCaseIncluded()))
.isEqualTo(IncludeUseCaseUsageProjectData.SemanticIds.INCLUDED_USE_CASE_2_ID);
assertThat(this.identityService.getId(includeUsage.getOwningUsage()))
.isEqualTo(IncludeUseCaseUsageProjectData.SemanticIds.INCLUDING_USE_CASE_ID);
});
}

@Test
@DisplayName("GIVEN an IncludeUseCaseUsage, WHEN reconnecting the target, THEN the new target of the IncludeUseCaseUsage is correct")
@Sql(scripts = { IncludeUseCaseUsageProjectData.SCRIPT_PATH }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED))
@Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void reconnectIncludeUsaceCaseUsageTarget() {
this.verifier.then(() -> this.edgeReconnectionTester.reconnectEdge(IncludeUseCaseUsageProjectData.EDITING_CONTEXT_ID,
this.diagram,
IncludeUseCaseUsageProjectData.GraphicalIds.INCLUDE_USE_CASE_USAGE_ID,
IncludeUseCaseUsageProjectData.GraphicalIds.INCLUDED_USE_CASE_2_ID,
ReconnectEdgeKind.TARGET));

IDiagramChecker diagramCheckerTarget = (initialDiagram, newDiagram) -> {
new CheckDiagramElementCount(this.diagramComparator)
.hasNewEdgeCount(1)
.check(initialDiagram, newDiagram);

Edge newEdge = this.diagramComparator.newEdges(initialDiagram, newDiagram).get(0);
assertThat(newEdge).hasSourceId(IncludeUseCaseUsageProjectData.GraphicalIds.INCLUDING_USE_CASE_ID);
assertThat(newEdge).hasTargetId(IncludeUseCaseUsageProjectData.GraphicalIds.INCLUDED_USE_CASE_2_ID);
};

this.diagramCheckerService.checkDiagram(diagramCheckerTarget, this.diagram, this.verifier);

this.semanticCheckerService.checkElement(this.verifier, IncludeUseCaseUsage.class, () -> IncludeUseCaseUsageProjectData.SemanticIds.INCLUDE_USE_CASE_USAGE_ID, includeUsage -> {
assertThat(this.identityService.getId(includeUsage.getUseCaseIncluded()))
.isEqualTo(IncludeUseCaseUsageProjectData.SemanticIds.INCLUDED_USE_CASE_2_ID);
assertThat(this.identityService.getId(includeUsage.getOwningUsage()))
.isEqualTo(IncludeUseCaseUsageProjectData.SemanticIds.INCLUDING_USE_CASE_ID);
});
}

@Test
@DisplayName("GIVEN an IncludeUseCaseUsage, WHEN reconnecting the source, THEN the new source of the IncludeUseCaseUsage is correct")
@Sql(scripts = { IncludeUseCaseUsageProjectData.SCRIPT_PATH }, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED))
@Sql(scripts = { "/scripts/cleanup.sql" }, executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD, config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED))
public void reconnectIncludeUseCaseUsageSource() {
this.verifier.then(() -> this.edgeReconnectionTester.reconnectEdge(IncludeUseCaseUsageProjectData.EDITING_CONTEXT_ID,
this.diagram,
IncludeUseCaseUsageProjectData.GraphicalIds.INCLUDE_USE_CASE_USAGE_ID,
IncludeUseCaseUsageProjectData.GraphicalIds.INCLUDED_USE_CASE_2_ID,
ReconnectEdgeKind.SOURCE));

IDiagramChecker diagramCheckerTarget = (initialDiagram, newDiagram) -> {
new CheckDiagramElementCount(this.diagramComparator)
.hasNewNodeCount(1) // New element in the Perform action compartment
.hasNewEdgeCount(1)
.check(initialDiagram, newDiagram);

Edge newEdge = this.diagramComparator.newEdges(initialDiagram, newDiagram).get(0);
assertThat(newEdge).hasSourceId(IncludeUseCaseUsageProjectData.GraphicalIds.INCLUDED_USE_CASE_2_ID);
assertThat(newEdge).hasTargetId(IncludeUseCaseUsageProjectData.GraphicalIds.INCLUDED_USE_CASE_ID);
};

this.diagramCheckerService.checkDiagram(diagramCheckerTarget, this.diagram, this.verifier);

this.semanticCheckerService.checkElement(this.verifier, IncludeUseCaseUsage.class, () -> IncludeUseCaseUsageProjectData.SemanticIds.INCLUDE_USE_CASE_USAGE_ID, includeUsage -> {
assertThat(this.identityService.getId(includeUsage.getUseCaseIncluded()))
.isEqualTo(IncludeUseCaseUsageProjectData.SemanticIds.INCLUDED_USE_CASE_ID);
assertThat(this.identityService.getId(includeUsage.getOwningUsage()))
.isEqualTo(IncludeUseCaseUsageProjectData.SemanticIds.INCLUDED_USE_CASE_2_ID);
});
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*******************************************************************************
* Copyright (c) 2025 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.data;

/**
* Project data for the "GeneralView-IncludeUseCaseUsage" project.
*
* @author Arthur Daussy
*/
public class IncludeUseCaseUsageProjectData {

public static final String SCRIPT_PATH = "/scripts/database-content/GeneralView-IncludeUseCaseUsage.sql";

public static final String EDITING_CONTEXT_ID = "b3bd124d-4b4a-4e92-8790-1b959b0be7b9";

/**
* Ids for the semantic elements.
*/
public static final class SemanticIds {

public static final String PACKAGE_1_ID = "34f7c6c2-a2ab-4696-a208-c37197532604";

public static final String INCLUDING_USE_CASE_ID = "711a34eb-e163-4ccb-88f0-190804493003";

public static final String INCLUDED_USE_CASE_ID = "3992c28f-e44c-44db-90cd-81ea5f3ac5db";

public static final String INCLUDED_USE_CASE_2_ID = "397cfe49-c10b-4949-b0a6-fc0be0b4f84b";

public static final String INCLUDE_USE_CASE_USAGE_ID = "d5406336-a52c-4c08-9acd-a85805d6714d";
}

/**
* Ids of graphical elements.
*/
public static class GraphicalIds {

public static final String DIAGRAM_ID = "a73cb836-d14d-426f-a67d-10df77f92809";

public static final String INCLUDING_USE_CASE_ID = "49fe4aa7-7662-3046-a84f-54a3a7a71704";

public static final String INCLUDED_USE_CASE_ID = "61ec2096-fb65-31cb-a440-a3916a004c42";

public static final String INCLUDED_USE_CASE_2_ID = "99a23d3a-a37d-387b-a3f1-b57dd5b23e32";

public static final String INCLUDE_USE_CASE_USAGE_ID = "1cfff221-87b4-302d-967a-d37551571939";
}

}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import org.eclipse.syson.sysml.FeatureMembership;
import org.eclipse.syson.sysml.FeatureTyping;
import org.eclipse.syson.sysml.FlowConnectionUsage;
import org.eclipse.syson.sysml.IncludeUseCaseUsage;
import org.eclipse.syson.sysml.InterfaceUsage;
import org.eclipse.syson.sysml.ItemDefinition;
import org.eclipse.syson.sysml.ItemUsage;
Expand Down Expand Up @@ -494,6 +495,29 @@ public Element createPartUsageAsAllocationDefinitionEnd(AllocationDefinition sel
return self;
}

/**
* Create a new IncludeUseCaseUsage.
*
* @param source
* the source usage.
* @param target
* the target usage.
* @return a new {@link IncludeUseCaseUsage}.
*/
public IncludeUseCaseUsage createIncludeUseCaseUsage(UseCaseUsage source, UseCaseUsage target) {
var ownerMembership = SysmlFactory.eINSTANCE.createFeatureMembership();
source.getOwnedRelationship().add(ownerMembership);

IncludeUseCaseUsage includeUsage = SysmlFactory.eINSTANCE.createIncludeUseCaseUsage();
ownerMembership.getOwnedRelatedElement().add(includeUsage);

ReferenceSubsetting refSub = SysmlFactory.eINSTANCE.createReferenceSubsetting();
includeUsage.getOwnedRelationship().add(refSub);

refSub.setReferencedFeature(target);
return includeUsage;
}

public Element createAllocateEdge(Element source, Element target, Node sourceNode, IEditingContext editingContext, IDiagramService diagramService) {
var owner = source.getOwner();
var ownerMembership = SysmlFactory.eINSTANCE.createOwningMembership();
Expand Down
Loading
Loading