Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ public static final class SemanticIds {
public static final String ACTION_USAGE_ID = "62481ceb-d036-4723-acda-2bace93ae370";

public static final String ACTION_DEFINITION_ID = "b918a8a1-f30b-4703-b131-77cde928401d";

public static final String OCCURRENCE_USAGE_ID = "5126fb57-f6b3-4d0c-b981-92cfc961091f";
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2024, 2025 Obeo.
* Copyright (c) 2024, 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
Expand All @@ -13,6 +13,7 @@
package org.eclipse.syson.metamodel.helper;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;

import java.time.Duration;
Expand All @@ -21,44 +22,44 @@
import java.util.Optional;
import java.util.UUID;

import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramEventInput;
import org.eclipse.sirius.components.collaborative.diagrams.dto.DiagramRefreshedEventPayload;
import org.eclipse.sirius.components.collaborative.dto.EditingContextEventInput;
import org.eclipse.sirius.components.core.api.IObjectSearchService;
import org.eclipse.sirius.components.graphql.tests.EditingContextEventSubscriptionRunner;
import org.eclipse.sirius.components.graphql.tests.ExecuteEditingContextFunctionSuccessPayload;
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.ISemanticChecker;
import org.eclipse.syson.application.controller.editingContext.checkers.SemanticCheckerService;
import org.eclipse.syson.application.data.GeneralViewWithTopNodesTestProjectData;
import org.eclipse.syson.services.SemanticRunnableFactory;
import org.eclipse.syson.services.diagrams.api.IGivenDiagramSubscription;
import org.eclipse.syson.sysml.AcceptActionUsage;
import org.eclipse.syson.sysml.ActionUsage;
import org.eclipse.syson.sysml.Element;
import org.eclipse.syson.sysml.OccurrenceUsage;
import org.eclipse.syson.sysml.PartDefinition;
import org.eclipse.syson.sysml.PartUsage;
import org.eclipse.syson.sysml.PortionKind;
import org.eclipse.syson.sysml.Redefinition;
import org.eclipse.syson.sysml.Specialization;
import org.eclipse.syson.sysml.SysmlFactory;
import org.eclipse.syson.sysml.helper.EMFUtils;
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;
import reactor.test.StepVerifier.Step;

/**
* Tests the implicit specializations of SysMLv2 elements. We need an integration test to benefit from the standard
* libraries.
*
* @author arichard
*/
@SuppressWarnings("checkstyle:MultipleStringLiterals")
@Transactional
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class ImplicitSpecializationsTests extends AbstractIntegrationTests {
Expand All @@ -67,44 +68,28 @@ public class ImplicitSpecializationsTests extends AbstractIntegrationTests {
private IGivenInitialServerState givenInitialServerState;

@Autowired
private IGivenDiagramSubscription givenDiagramSubscription;
private EditingContextEventSubscriptionRunner editingContextEventSubscriptionRunner;

@Autowired
private IObjectSearchService objectSearchService;

@Autowired
private SemanticRunnableFactory semanticRunnableFactory;

private Step<DiagramRefreshedEventPayload> verifier;

private SemanticCheckerService semanticCheckerService;

@BeforeEach
public void setUp() {
this.givenInitialServerState.initialize();
var diagramEventInput = new DiagramEventInput(UUID.randomUUID(),
GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID,
GeneralViewWithTopNodesTestProjectData.GraphicalIds.DIAGRAM_ID);
var flux = this.givenDiagramSubscription.subscribe(diagramEventInput);
this.verifier = StepVerifier.create(flux);
this.semanticCheckerService = new SemanticCheckerService(this.semanticRunnableFactory, this.objectSearchService, GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID,
GeneralViewWithTopNodesTestProjectData.SemanticIds.PACKAGE_1_ID);
}

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

@DisplayName("GIVEN a PartUsage with or without Specialization (FeatureTyping, Susbetting or Redefinition), WHEN validation is executed, THEN the Part implictly specializes 'Parts::parts' from the standard libraries.")
@Sql(scripts = { GeneralViewWithTopNodesTestProjectData.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))
@DisplayName("GIVEN a PartUsage with or without Specialization (FeatureTyping, Subsetting or Redefinition), WHEN validation is executed, THEN the Part implicitly specializes 'Parts::parts' from the standard libraries.")
@GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH })
@Test
public void testImplicitSpecializationOnPartUsage() {
var semanticCheckerService = new SemanticCheckerService(this.semanticRunnableFactory, this.objectSearchService, GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID,
GeneralViewWithTopNodesTestProjectData.SemanticIds.PACKAGE_1_ID);
var editingContextEventInput = new EditingContextEventInput(UUID.randomUUID(), GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID);
var flux = this.editingContextEventSubscriptionRunner.run(editingContextEventInput).flux();

ISemanticChecker semanticChecker = (editingContext) -> {
Object semanticRootObject = this.objectSearchService.getObject(editingContext, GeneralViewWithTopNodesTestProjectData.SemanticIds.PACKAGE_1_ID).orElse(null);
assertThat(semanticRootObject).isInstanceOf(Element.class);
Expand Down Expand Up @@ -149,15 +134,21 @@ public void testImplicitSpecializationOnPartUsage() {
assertTrue(anotherPart.specializesFromLibrary("Parts::Part"));
};

this.semanticCheckerService.checkEditingContext(semanticChecker, this.verifier);
StepVerifier.create(flux)
.then(semanticCheckerService.checkEditingContext(semanticChecker))
.thenCancel()
.verify(Duration.ofSeconds(10));
}

@DisplayName("GIVEN an AcceptAction, WHEN the validation is executed, THEN the AcceptAction implictly specializes 'Actions::acceptActions'")
@Sql(scripts = { GeneralViewWithTopNodesTestProjectData.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))
@DisplayName("GIVEN an AcceptAction, WHEN the validation is executed, THEN the AcceptAction implicitly specializes 'Actions::acceptActions'")
@GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH })
@Test
public void testImplicitSpecializationOnAcceptActionUsage() {
var semanticCheckerService = new SemanticCheckerService(this.semanticRunnableFactory, this.objectSearchService, GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID,
GeneralViewWithTopNodesTestProjectData.SemanticIds.PACKAGE_1_ID);
var editingContextEventInput = new EditingContextEventInput(UUID.randomUUID(), GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID);
var flux = this.editingContextEventSubscriptionRunner.run(editingContextEventInput).flux();

ISemanticChecker semanticChecker = (editingContext) -> {
Object semanticRootObject = this.objectSearchService.getObject(editingContext, GeneralViewWithTopNodesTestProjectData.SemanticIds.PACKAGE_1_ID).orElse(null);
assertThat(semanticRootObject).isInstanceOf(Element.class);
Expand All @@ -171,15 +162,21 @@ public void testImplicitSpecializationOnAcceptActionUsage() {
assertTrue(acceptActionUsage.specializesFromLibrary("Actions::acceptActions"));
};

this.semanticCheckerService.checkEditingContext(semanticChecker, this.verifier);
StepVerifier.create(flux)
.then(semanticCheckerService.checkEditingContext(semanticChecker))
.thenCancel()
.verify(Duration.ofSeconds(10));
}

@DisplayName("GIVEN a parameter of an ActionUsage that subsets another ActionUsage with parameters, WHEN the validation is executed, THEN the ActionUsgae implicitly redefines the corresponding parameter")
@Sql(scripts = { GeneralViewWithTopNodesTestProjectData.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))
@GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH })
@Test
public void testImplicitParameterRedefinitionOnItemUsage() {
var semanticCheckerService = new SemanticCheckerService(this.semanticRunnableFactory, this.objectSearchService, GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID,
GeneralViewWithTopNodesTestProjectData.SemanticIds.PACKAGE_1_ID);
var editingContextEventInput = new EditingContextEventInput(UUID.randomUUID(), GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID);
var flux = this.editingContextEventSubscriptionRunner.run(editingContextEventInput).flux();

ISemanticChecker semanticChecker = (editingContext) -> {
Object semanticRootObject = this.objectSearchService.getObject(editingContext, GeneralViewWithTopNodesTestProjectData.SemanticIds.PACKAGE_1_ID).orElse(null);
assertThat(semanticRootObject).isInstanceOf(Element.class);
Expand Down Expand Up @@ -207,6 +204,86 @@ public void testImplicitParameterRedefinitionOnItemUsage() {
.extracting(Redefinition::getRedefinedFeature)
.isEqualTo(parentActionWithParameter.getParameter().get(0));
};
this.semanticCheckerService.checkEditingContext(semanticChecker, this.verifier);

StepVerifier.create(flux)
.then(semanticCheckerService.checkEditingContext(semanticChecker))
.thenCancel()
.verify(Duration.ofSeconds(10));
}

@DisplayName("GIVEN a timeslice Part, WHEN the validation is executed, THEN the Part implicitly specializes 'Occurrences::Occurrence::timeSlices'")
@GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH })
@Test
public void testImplicitTimeSliceSpecializationOnPartUsage() {
var semanticCheckerService = new SemanticCheckerService(this.semanticRunnableFactory, this.objectSearchService, GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID,
GeneralViewWithTopNodesTestProjectData.SemanticIds.PACKAGE_1_ID);

var editingContextEventInput = new EditingContextEventInput(UUID.randomUUID(), GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID);
var flux = this.editingContextEventSubscriptionRunner.run(editingContextEventInput).flux();

var initializeEditingContext = this.semanticRunnableFactory.createRunnable(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, (editingContext, executeEditingContextFunctionInput) -> {
var optionalPartUsage = this.objectSearchService.getObject(editingContext, GeneralViewWithTopNodesTestProjectData.SemanticIds.PART_USAGE_ID)
.filter(PartUsage.class::isInstance)
.map(PartUsage.class::cast);
assertThat(optionalPartUsage).isPresent();
var partUsage = optionalPartUsage.get();
assertFalse(partUsage.specializesFromLibrary("Occurrences::Occurrence::timeSlices"));
partUsage.setPortionKind(PortionKind.TIMESLICE);
return new ExecuteEditingContextFunctionSuccessPayload(executeEditingContextFunctionInput.id(), true);
});

ISemanticChecker semanticChecker = (editingContext) -> {
var optionalPartUsage = this.objectSearchService.getObject(editingContext, GeneralViewWithTopNodesTestProjectData.SemanticIds.PART_USAGE_ID)
.filter(PartUsage.class::isInstance)
.map(PartUsage.class::cast);
assertThat(optionalPartUsage).isPresent();
var partUsage = optionalPartUsage.get();
assertTrue(partUsage.specializesFromLibrary("Parts::Part"));
assertTrue(partUsage.specializesFromLibrary("Occurrences::Occurrence::timeSlices"));
};

StepVerifier.create(flux)
.then(initializeEditingContext)
.then(semanticCheckerService.checkEditingContext(semanticChecker))
.thenCancel()
.verify(Duration.ofSeconds(10));
}

@DisplayName("GIVEN a snapshot Occurrence, WHEN the validation is executed, THEN the Occurrence implicitly specializes 'Occurrences::Occurrence::snapshot'")
@GivenSysONServer({ GeneralViewWithTopNodesTestProjectData.SCRIPT_PATH })
@Test
public void testImplicitTimeSliceSpecializationOnOccurrenceUsage() {
var semanticCheckerService = new SemanticCheckerService(this.semanticRunnableFactory, this.objectSearchService, GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID,
GeneralViewWithTopNodesTestProjectData.SemanticIds.PACKAGE_1_ID);
var editingContextEventInput = new EditingContextEventInput(UUID.randomUUID(), GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID);
var flux = this.editingContextEventSubscriptionRunner.run(editingContextEventInput).flux();

var initializeEditingContext = this.semanticRunnableFactory.createRunnable(GeneralViewWithTopNodesTestProjectData.EDITING_CONTEXT_ID, (editingContext, executeEditingContextFunctionInput) -> {
var optionalOccurrenceUsage = this.objectSearchService.getObject(editingContext, GeneralViewWithTopNodesTestProjectData.SemanticIds.OCCURRENCE_USAGE_ID)
.filter(OccurrenceUsage.class::isInstance)
.map(OccurrenceUsage.class::cast);
assertThat(optionalOccurrenceUsage).isPresent();
var occurrenceUsage = optionalOccurrenceUsage.get();
assertTrue(occurrenceUsage.specializesFromLibrary("Occurrences::occurrences"));
assertFalse(occurrenceUsage.specializesFromLibrary("Occurrences::Occurrence::snapshots"));
occurrenceUsage.setPortionKind(PortionKind.SNAPSHOT);
return new ExecuteEditingContextFunctionSuccessPayload(executeEditingContextFunctionInput.id(), true);
});

ISemanticChecker semanticChecker = (editingContext) -> {
var optionalOccurrenceUsage = this.objectSearchService.getObject(editingContext, GeneralViewWithTopNodesTestProjectData.SemanticIds.OCCURRENCE_USAGE_ID)
.filter(OccurrenceUsage.class::isInstance)
.map(OccurrenceUsage.class::cast);
assertThat(optionalOccurrenceUsage).isPresent();
var occurrenceUsage = optionalOccurrenceUsage.get();
assertTrue(occurrenceUsage.specializesFromLibrary("Occurrences::occurrences"));
assertTrue(occurrenceUsage.specializesFromLibrary("Occurrences::Occurrence::snapshots"));
};

StepVerifier.create(flux)
.then(initializeEditingContext)
.then(semanticCheckerService.checkEditingContext(semanticChecker))
.thenCancel()
.verify(Duration.ofSeconds(10));
}
}
Loading
Loading