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
2 changes: 2 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ The property `SysONTestsProperties#ELASTICSEARCH` has been removed, tests that r

- [releng] Update to https://github.com/eclipse-sirius/sirius-web[Sirius Web 2026.1.2]
- [releng] Update to https://github.com/spring-projects/spring-boot/releases/tag/v3.5.10[Spring Boot 3.5.10]
- [releng] Update to archunit-junit5 1.3.0

=== Bug fixes

Expand All @@ -36,6 +37,7 @@ The property `SysONTestsProperties#ELASTICSEARCH` has been removed, tests that r
- https://github.com/eclipse-syson/syson/issues/1973[#1973] [import] Fix an error during textual import when resolving the name of an unnamed redefined `Feature`.
- https://github.com/eclipse-syson/syson/issues/1860[#1860] [diagrams] Add a precondition for compartment item node descriptions in order to filter out unwanted types.
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.

=== Improvements

Expand Down
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 @@ -16,11 +16,13 @@

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.UUID;

import org.apache.commons.io.FileUtils;
import org.eclipse.sirius.web.tests.services.api.IGivenInitialServerState;
import org.eclipse.syson.AbstractIntegrationTests;
import org.eclipse.syson.GivenSysONServer;
import org.eclipse.syson.application.data.SimpleProjectElementsTestProjectData;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
Expand All @@ -29,8 +31,6 @@
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.core.io.ClassPathResource;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.jdbc.SqlConfig;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -61,14 +61,13 @@ public void beforeEach() {
this.givenInitialServerState.initialize();
}

@Test
@DisplayName("GIVEN the SysON REST API, WHEN we ask for all changes, THEN all changes should be returned")
@Sql(scripts = { SimpleProjectElementsTestProjectData.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({ SimpleProjectElementsTestProjectData.SCRIPT_PATH })
@Test
public void givenSysONRestAPIWhenWeAskForAllChangesThenAllChangesShouldBeReturned() {
var webTestClient = WebTestClient.bindToServer()
.baseUrl(this.getHTTPBaseUrl())
.responseTimeout(Duration.ofSeconds(30))
.build();

String expectedJSON = null;
Expand All @@ -89,14 +88,13 @@ public void givenSysONRestAPIWhenWeAskForAllChangesThenAllChangesShouldBeReturne
.json(expectedJSON);
}

@Test
@DisplayName("GIVEN the SysON REST API, WHEN we ask for all changes in an unknown project, THEN it should return an error")
@Sql(scripts = { SimpleProjectElementsTestProjectData.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({ SimpleProjectElementsTestProjectData.SCRIPT_PATH })
@Test
public void givenSysONRestAPIWhenWeAskForAllChangesInUnknownProjectThenItShouldReturnAnError() {
var webTestClient = WebTestClient.bindToServer()
.baseUrl(this.getHTTPBaseUrl())
.responseTimeout(Duration.ofSeconds(30))
.build();

var uri = String.format("/api/rest/projects/%s/commits/%s/changes", INVALID_PROJECT, INVALID_PROJECT);
Expand All @@ -107,14 +105,13 @@ public void givenSysONRestAPIWhenWeAskForAllChangesInUnknownProjectThenItShouldR
.isNotFound();
}

@Test
@DisplayName("GIVEN the SysON REST API, WHEN we ask for changes of a specific element, THEN those changes should be returned")
@Sql(scripts = { SimpleProjectElementsTestProjectData.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({ SimpleProjectElementsTestProjectData.SCRIPT_PATH })
@Test
public void givenSysONRestAPIWhenWeAskForChangesOfASpecificElementThenThoseChangesShouldBeReturned() {
var webTestClient = WebTestClient.bindToServer()
.baseUrl(this.getHTTPBaseUrl())
.responseTimeout(Duration.ofSeconds(30))
.build();

String expectedJSON = null;
Expand All @@ -136,14 +133,13 @@ public void givenSysONRestAPIWhenWeAskForChangesOfASpecificElementThenThoseChang
.json(expectedJSON);
}

@Test
@DisplayName("GIVEN the SysON REST API, WHEN we ask for specific changes in an unknown project, THEN it should return an error")
@Sql(scripts = { SimpleProjectElementsTestProjectData.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({ SimpleProjectElementsTestProjectData.SCRIPT_PATH })
@Test
public void givenSysONRestAPIWhenWeAskForSpecificChangesInUnknownProjectThenItShouldReturnAnError() {
var webTestClient = WebTestClient.bindToServer()
.baseUrl(this.getHTTPBaseUrl())
.responseTimeout(Duration.ofSeconds(30))
.build();

var computedChangeId = UUID.nameUUIDFromBytes((INVALID_PROJECT + SimpleProjectElementsTestProjectData.SemanticIds.PART_ID).getBytes()).toString();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright (c) 2023, 2025 Obeo.
* Copyright (c) 2023, 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 Down Expand Up @@ -547,36 +547,64 @@ public void setOwningRelationship(Relationship newOwningRelationship) {
* according to the KerML textual concrete syntax for qualified names (including use of unrestricted name notation
* and escaped characters, as necessary). The qualifiedName is null if this Element has no owningNamespace or if
* there is not a complete ownership chain of named Namespaces from a root Namespace to this Element. <!--
* end-user-doc -->
* end-user-doc
* <p>
* qualifiedName = <br/>
* if owningNamespace = null then null <br/>
* else if name <> null and owningNamespace.ownedMember-> select(m | m.name = name).indexOf(self) <> 1 then null
* else if owningNamespace.owner = null then escapedName() <br/>
* else if owningNamespace.qualifiedName = null or escapedName() = null then null <br/>
* else owningNamespace.qualifiedName + '::' + escapedName() <br/>
* endif <br/>
* endif <br/>
* endif <br/>
* endif
* </p>
* -->
*
* @generated NOT
*/
@Override
public String getQualifiedName() {
String selfName = NameHelper.toPrintableName(this.getName());
// SysMLv2 specification compliant implementation
// at some point we will have to use this implementation
// String getQualifiedName = null;
// if (this.getOwningNamespace() == null) {
// getQualifiedName = null;
// } else if (this.getName() != null && this.getOwningNamespace().getOwnedMember().stream().filter(m ->
// m.getName() == this.getName()).toList().indexOf(this) != 0) {
// getQualifiedName = null;
// } else if (this.getOwningNamespace().getOwner() != null) {
// getQualifiedName = this.escapedName();
// } else if (this.getOwningNamespace().getQualifiedName() == null || this.escapedName() == null) {
// getQualifiedName = null;
// } else {
// getQualifiedName = this.getOwningNamespace().getQualifiedName() + "::" + this.escapedName();
// }
// return getQualifiedName;

var selfName = NameHelper.toPrintableName(this.getName(), true);
if (selfName.isBlank()) {
return null;
}

StringBuilder qualifiedNameBuilder = new StringBuilder();
Element container = this.getOwner();
if (container != null && container instanceof Membership membership) {
Element membershipContainer = membership.getOwner();
if (membershipContainer != null) {
String elementQN = membershipContainer.getQualifiedName();
var qualifiedNameBuilder = new StringBuilder();
var container = this.getOwner();
if (container instanceof Membership membership) {
var membershipContainer = membership.getOwner();
if (membershipContainer instanceof Element) {
var elementQN = membershipContainer.getQualifiedName();
if (elementQN != null && !elementQN.isBlank()) {
qualifiedNameBuilder.append(elementQN);
qualifiedNameBuilder.append("::");
}
}
} else if (container != null) {
String elementQN = container.getQualifiedName();
var elementQN = container.getQualifiedName();
if (elementQN != null && !elementQN.isBlank()) {
qualifiedNameBuilder.append(elementQN);
qualifiedNameBuilder.append("::");
}
}

qualifiedNameBuilder.append(selfName);
return qualifiedNameBuilder.toString();
}
Expand Down Expand Up @@ -630,17 +658,11 @@ public String effectiveShortName() {
*/
@Override
public String escapedName() {
String escapedName = null;
String name = this.getName();
if (name == null) {
String escapedName = this.getName();
if (escapedName == null) {
escapedName = this.getShortName();
} else {
escapedName = this.getName();
}
if (escapedName != null && escapedName.contains("\\S+")) {
escapedName = "'" + escapedName + "'";
}
return escapedName;
return NameHelper.toPrintableName(escapedName, true);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ public String caseAllocationUsage(AllocationUsage allocationUsage) {
builder.appendWithSpaceIfNeeded(SysMLv2Keywords.ALLOCATION);
builder.appendWithSpaceIfNeeded(declarationBuilder);
}

builder.appendWithSpaceIfNeeded(SysMLv2Keywords.ALLOCATE);
this.appendConnectorPart(builder, allocationUsage);
this.appendChildrenContent(builder, allocationUsage, allocationUsage.getOwnedMembership());
Expand Down Expand Up @@ -866,6 +866,21 @@ public String casePortUsage(PortUsage portUsage) {
return builder.toString();
}

@Override
public String caseReferenceUsage(ReferenceUsage reference) {
Appender builder = new Appender(this.lineSeparator, this.indentation);
if (!this.isImplicit(reference)) {
this.appendBasicUsagePrefix(builder, reference);
this.appendUsageDeclaration(builder, reference);
this.appendUsageCompletion(builder, reference);
}
if (builder.toString().equals(";")) {
// This an EmptyUsage rule
return "";
}
return builder.toString();
}

@Override
public String caseRenderingUsage(RenderingUsage rendering) {
Appender builder = new Appender(this.lineSeparator, this.indentation);
Expand Down Expand Up @@ -908,21 +923,6 @@ public String caseRequirementDefinition(RequirementDefinition requirement) {
return builder.toString();
}

@Override
public String caseReferenceUsage(ReferenceUsage reference) {
Appender builder = new Appender(this.lineSeparator, this.indentation);
if (!this.isImplicit(reference)) {
this.appendBasicUsagePrefix(builder, reference);
this.appendUsageDeclaration(builder, reference);
this.appendUsageCompletion(builder, reference);
}
if (builder.toString().equals(";")) {
// This an EmptyUsage rule
return "";
}
return builder.toString();
}

@Override
public String caseRequirementUsage(RequirementUsage usage) {
Appender builder = this.newAppender();
Expand Down
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 Down Expand Up @@ -34,7 +34,7 @@
*
* @author gescande
*/
public class ElementTest {
public class ElementImplTest {

/**
* Test model
Expand All @@ -51,9 +51,10 @@ public class ElementTest {
* }
* private part def 'private def1x1';
* private part def 'def-10';
* private part def 'éléphant;
* private part def 'éléphant';
* private part def def_11;
* private part def _def_12;
* part def 'with escaped\'apostrophe';
* }
* }
* }
Expand All @@ -70,14 +71,11 @@ private class TestModel {
private Package p1x1;
private PartDefinition def1x1;
private PartDefinition privatedef1x1;

private PartDefinition def10;

private PartDefinition defElephant;

private PartDefinition def11;

private PartDefinition def12;
private PartDefinition withEscapedApostrophe;

TestModel() {
this.build();
Expand All @@ -97,6 +95,7 @@ private void build() {
this.defElephant = this.builder.createInWithName(PartDefinition.class, this.p1x1, "éléphant");
this.def11 = this.builder.createInWithName(PartDefinition.class, this.p1x1, "def_11");
this.def12 = this.builder.createInWithName(PartDefinition.class, this.p1x1, "_def_12");
this.withEscapedApostrophe = this.builder.createInWithName(PartDefinition.class, this.p1x1, "with escaped'apostrophe");
}
}

Expand All @@ -113,6 +112,7 @@ public void getQualifiedNameTest() {
assertEquals("p1::'p1 x1'::'éléphant'", testModel.defElephant.getQualifiedName());
assertEquals("p1::'p1 x1'::def_11", testModel.def11.getQualifiedName());
assertEquals("p1::'p1 x1'::_def_12", testModel.def12.getQualifiedName());
assertEquals("p1::'p1 x1'::'with escaped\\'apostrophe'", testModel.withEscapedApostrophe.getQualifiedName());
}

@DisplayName("Check documentation feature")
Expand Down Expand Up @@ -140,4 +140,11 @@ public void isLibraryElementNotInsideLibraryPackageTest() {
assertThat(testModel.def1.isIsLibraryElement()).isFalse();
assertThat(testModel.def12.isIsLibraryElement()).isFalse();
}

@DisplayName("Check espacedNamed derived operation")
@Test
public void escapedNameTest() {
var testModel = new TestModel();
assertEquals("'with escaped\\'apostrophe'", testModel.withEscapedApostrophe.escapedName());
}
}
2 changes: 1 addition & 1 deletion backend/tests/syson-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
<dependency>
<groupId>com.tngtech.archunit</groupId>
<artifactId>archunit-junit5</artifactId>
<version>0.19.0</version>
<version>1.3.0</version>
</dependency>
</dependencies>
<build>
Expand Down
Loading
Loading