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 @@ -39,6 +39,7 @@ The property `SysONTestsProperties#ELASTICSEARCH` has been removed, tests that r
For example `ViewUsage` elements are no longer rendered in _parts_ compartments.
- https://github.com/eclipse-syson/syson/issues/1981[#1981] [export] Fix an error during textual export where `Expose` elements with apostrophes in their name were not properly escaped.
- https://github.com/eclipse-syson/syson/issues/1983[#1983] [metamodel] `reqId` and `declaredShortName` properties of `RequirementDefinition` and `RequirementUsage` are now synchronized, as required by the SysMLv2 specification.
- https://github.com/eclipse-syson/syson/issues/2014[#2014] [diagrams] Forbid the expose of a `ViewUsage` in this same `ViewUsage`, directly or indirectly (i.e. through others exposed `ViewUsages`).

=== Improvements

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
/*******************************************************************************
* 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.eclipse.sirius.components.diagrams.tests.DiagramEventPayloadConsumer.assertRefreshedDiagramThat;

import java.time.Duration;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

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.Diagram;
import org.eclipse.sirius.components.diagrams.tests.navigation.DiagramNavigator;
import org.eclipse.sirius.components.graphql.tests.ExecuteEditingContextFunctionInput;
import org.eclipse.sirius.components.graphql.tests.ExecuteEditingContextFunctionSuccessPayload;
import org.eclipse.sirius.components.graphql.tests.api.IExecuteEditingContextFunctionRunner;
import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState;
import org.eclipse.syson.AbstractIntegrationTests;
import org.eclipse.syson.GivenSysONServer;
import org.eclipse.syson.application.controllers.diagrams.testers.DropFromExplorerTester;
import org.eclipse.syson.application.data.GVViewUsageCircularExposeTestProjectData;
import org.eclipse.syson.services.diagrams.api.IGivenDiagramSubscription;
import org.eclipse.syson.sysml.ViewUsage;
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;

/**
* Tests the expose of ViewUsage inside in the General View diagram.
*
* @author arichard
*/
@Transactional
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class GVViewUsageCircularExposeTests extends AbstractIntegrationTests {

@Autowired
private IGivenInitialServerState givenInitialServerState;

@Autowired
private IGivenDiagramSubscription givenDiagramSubscription;

@Autowired
private DropFromExplorerTester dropFromExplorerTester;

@Autowired
private IExecuteEditingContextFunctionRunner executeEditingContextFunctionRunner;

@Autowired
private IObjectSearchService objectSearchService;

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

@BeforeEach
public void beforeEach() {
this.givenInitialServerState.initialize();
}

@DisplayName("GIVEN a diagram with ViewUsage nodes, WHEN the ViewUsage corresponding to the diagram is d&d into the diagram, THEN it is not exposed.")
@GivenSysONServer({ GVViewUsageCircularExposeTestProjectData.SCRIPT_PATH })
@Test
public void testDropViewUsageOnDiagram() {
var flux = this.givenSubscriptionToDiagram();

var diagram = new AtomicReference<Diagram>();

Consumer<Object> initialDiagramContentConsumer = assertRefreshedDiagramThat(diag -> {
diagram.set(diag);
assertThat(new DiagramNavigator(diag).findDiagramNodeCount()).isEqualTo(2);
});

Runnable dropFromExplorerRunnable = () -> {
this.dropFromExplorerTester.dropFromExplorerOnDiagram(GVViewUsageCircularExposeTestProjectData.EDITING_CONTEXT_ID, diagram,
GVViewUsageCircularExposeTestProjectData.SemanticIds.VIEW_USAGE_VIEW1_ID);
};

Consumer<Object> updatedDiagramContentConsumer = assertRefreshedDiagramThat(diag -> {
assertThat(new DiagramNavigator(diag).findDiagramNodeCount()).isEqualTo(2);
});

Runnable checkSemanticData = () -> {
var input = new ExecuteEditingContextFunctionInput(UUID.randomUUID(), GVViewUsageCircularExposeTestProjectData.EDITING_CONTEXT_ID,
(editingContext, executeEditingContextFunctionInput) -> {
Optional<Object> optViewUsage1 = this.objectSearchService.getObject(editingContext, GVViewUsageCircularExposeTestProjectData.SemanticIds.VIEW_USAGE_VIEW1_ID);
assertThat(optViewUsage1).isPresent().get().isInstanceOf(ViewUsage.class);
ViewUsage viewUsage1 = (ViewUsage) optViewUsage1.get();
assertThat(viewUsage1.getExposedElement()).hasSize(1);
assertThat(viewUsage1.getExposedElement()).doesNotContain(viewUsage1);
return new ExecuteEditingContextFunctionSuccessPayload(executeEditingContextFunctionInput.id(), true);
});
var payload = this.executeEditingContextFunctionRunner.execute(input).block();
assertThat(payload).isInstanceOf(ExecuteEditingContextFunctionSuccessPayload.class);
};

StepVerifier.create(flux)
.consumeNextWith(initialDiagramContentConsumer)
.then(dropFromExplorerRunnable)
.consumeNextWith(updatedDiagramContentConsumer)
.then(checkSemanticData)
.thenCancel()
.verify(Duration.ofSeconds(10));
}

@DisplayName("GIVEN a diagram with ViewUsage nodes, WHEN the ViewUsage corresponding to the diagram is d&d into a ViewUsage node, THEN it is not exposed.")
@GivenSysONServer({ GVViewUsageCircularExposeTestProjectData.SCRIPT_PATH })
@Test
public void testDropViewUsageOnViewUsageNode() {
var flux = this.givenSubscriptionToDiagram();

var diagram = new AtomicReference<Diagram>();

Consumer<Object> initialDiagramContentConsumer = assertRefreshedDiagramThat(diag -> {
diagram.set(diag);
assertThat(new DiagramNavigator(diag).findDiagramNodeCount()).isEqualTo(2);
});

Runnable dropFromExplorerOnTopNodeRunnable = () -> {
this.dropFromExplorerTester.dropFromExplorerOnDiagramElement(GVViewUsageCircularExposeTestProjectData.EDITING_CONTEXT_ID, diagram,
GVViewUsageCircularExposeTestProjectData.SemanticIds.VIEW_USAGE_VIEW1_ID, GVViewUsageCircularExposeTestProjectData.GraphicalIds.VIEW_USAGE_VIEW2_ID);
};

Consumer<Object> updatedDiagramContentConsumerAfterDropOnTopNode = assertRefreshedDiagramThat(diag -> {
assertThat(new DiagramNavigator(diag).findDiagramNodeCount()).isEqualTo(2);
});

Runnable dropFromExplorerOnNestedNodeRunnable = () -> {
this.dropFromExplorerTester.dropFromExplorerOnDiagramElement(GVViewUsageCircularExposeTestProjectData.EDITING_CONTEXT_ID, diagram,
GVViewUsageCircularExposeTestProjectData.SemanticIds.VIEW_USAGE_VIEW1_ID, GVViewUsageCircularExposeTestProjectData.GraphicalIds.VIEW_USAGE_VIEW3_ID);
};

Consumer<Object> updatedDiagramContentConsumerAfterDropOnNestedNode = assertRefreshedDiagramThat(diag -> {
assertThat(new DiagramNavigator(diag).findDiagramNodeCount()).isEqualTo(2);
});

Runnable checkSemanticData = () -> {
var input = new ExecuteEditingContextFunctionInput(UUID.randomUUID(), GVViewUsageCircularExposeTestProjectData.EDITING_CONTEXT_ID,
(editingContext, executeEditingContextFunctionInput) -> {
Optional<Object> optViewUsage1 = this.objectSearchService.getObject(editingContext, GVViewUsageCircularExposeTestProjectData.SemanticIds.VIEW_USAGE_VIEW1_ID);
assertThat(optViewUsage1).isPresent().get().isInstanceOf(ViewUsage.class);
ViewUsage viewUsage1 = (ViewUsage) optViewUsage1.get();
assertThat(viewUsage1.getExposedElement()).hasSize(1);
assertThat(viewUsage1.getExposedElement()).doesNotContain(viewUsage1);
return new ExecuteEditingContextFunctionSuccessPayload(executeEditingContextFunctionInput.id(), true);
});
var payload = this.executeEditingContextFunctionRunner.execute(input).block();
assertThat(payload).isInstanceOf(ExecuteEditingContextFunctionSuccessPayload.class);
};

StepVerifier.create(flux)
.consumeNextWith(initialDiagramContentConsumer)
.then(dropFromExplorerOnTopNodeRunnable)
.consumeNextWith(updatedDiagramContentConsumerAfterDropOnTopNode)
.then(dropFromExplorerOnNestedNodeRunnable)
.consumeNextWith(updatedDiagramContentConsumerAfterDropOnNestedNode)
.then(checkSemanticData)
.thenCancel()
.verify(Duration.ofSeconds(10));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,12 @@ public class DropFromExplorerTester {
@Autowired
private DropOnDiagramWithMessagesMutationRunner dropOnDiagramMutationRunner;

public void dropFromExplorerOnDiagram(String projectId, AtomicReference<Diagram> diagram, String semanticElementId) {
this.dropFromExplorer(projectId, diagram, null, semanticElementId);
public void dropFromExplorerOnDiagram(String projectId, AtomicReference<Diagram> diagram, String semanticElementIdToDnd) {
this.dropFromExplorer(projectId, diagram, null, semanticElementIdToDnd);
}

public void dropFromExplorerOnDiagramElement(String projectId, AtomicReference<Diagram> diagram, String semanticElementIdToDnd, String targetNodeId) {
this.dropFromExplorer(projectId, diagram, targetNodeId, semanticElementIdToDnd);
}

public GraphQLResult dropFromExplorer(String projectId, AtomicReference<Diagram> diagram, String targetNodeId, String semanticElementId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*******************************************************************************
* 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.data;

/**
* Identifiers for the "GV-ViewUsage-Circular-Expose" project.
*
* @author arichard
*/
public class GVViewUsageCircularExposeTestProjectData {

public static final String SCRIPT_PATH = "/scripts/database-content/GV-ViewUsage-Circular-Expose.sql";

public static final String EDITING_CONTEXT_ID = "089fc654-a72b-43da-8117-caeacc0f5f9b";

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

public static final String DIAGRAM_ID = "a7abf1bd-5208-48da-b870-9afe66487964";

public static final String VIEW_USAGE_VIEW2_ID = "4ec4159e-9a38-3f40-b9d8-7c40a212a818";

public static final String VIEW_USAGE_VIEW3_ID = "2b1584ea-dde1-3596-931d-40af44dbee69";
}

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

public static final String PACKAGE_1_ID = "351775b8-5dc8-4c08-9f71-a12fdcb0d616";

public static final String VIEW_USAGE_VIEW1_ID = "20a54630-ce64-4e08-a171-20b9f945ffdc";

public static final String VIEW_USAGE_VIEW2_ID = "5e778a4e-dd84-43a0-80c5-5c14c4ea96a7";

public static final String VIEW_USAGE_VIEW3_ID = "8d885c6b-49d2-4eae-b50d-5250cf56e5e1";
}

}
Loading
Loading