diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
index 26a654f08..f7f4f7281 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -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
@@ -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
diff --git a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/projects/ProjectDataVersioningRestControllerIntegrationTests.java b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/projects/ProjectDataVersioningRestControllerIntegrationTests.java
index acd4f4a4f..506dd0a68 100644
--- a/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/projects/ProjectDataVersioningRestControllerIntegrationTests.java
+++ b/backend/application/syson-application/src/test/java/org/eclipse/syson/application/controllers/projects/ProjectDataVersioningRestControllerIntegrationTests.java
@@ -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
@@ -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;
@@ -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;
@@ -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;
@@ -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);
@@ -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;
@@ -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();
diff --git a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/impl/ElementImpl.java b/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/impl/ElementImpl.java
index ecc7d158b..ab5f306af 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/impl/ElementImpl.java
+++ b/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/impl/ElementImpl.java
@@ -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
@@ -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
+ *
+ * qualifiedName =
+ * if owningNamespace = null then null
+ * else if name <> null and owningNamespace.ownedMember-> select(m | m.name = name).indexOf(self) <> 1 then null
+ * else if owningNamespace.owner = null then escapedName()
+ * else if owningNamespace.qualifiedName = null or escapedName() = null then null
+ * else owningNamespace.qualifiedName + '::' + escapedName()
+ * endif
+ * endif
+ * endif
+ * endif
+ *
+ * -->
*
* @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();
}
@@ -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);
}
/**
diff --git a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/SysMLElementSerializer.java b/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/SysMLElementSerializer.java
index 4e576c302..82a971882 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/SysMLElementSerializer.java
+++ b/backend/metamodel/syson-sysml-metamodel/src/main/java/org/eclipse/syson/sysml/textual/SysMLElementSerializer.java
@@ -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());
@@ -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);
@@ -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();
diff --git a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/impl/ElementTest.java b/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/impl/ElementImplTest.java
similarity index 88%
rename from backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/impl/ElementTest.java
rename to backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/impl/ElementImplTest.java
index fdaed3709..27bcecd5d 100644
--- a/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/impl/ElementTest.java
+++ b/backend/metamodel/syson-sysml-metamodel/src/test/java/org/eclipse/syson/sysml/impl/ElementImplTest.java
@@ -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
@@ -34,7 +34,7 @@
*
* @author gescande
*/
-public class ElementTest {
+public class ElementImplTest {
/**
* Test model
@@ -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';
* }
* }
* }
@@ -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();
@@ -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");
}
}
@@ -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")
@@ -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());
+ }
}
diff --git a/backend/tests/syson-tests/pom.xml b/backend/tests/syson-tests/pom.xml
index 4e8e8007f..8f9073cb1 100644
--- a/backend/tests/syson-tests/pom.xml
+++ b/backend/tests/syson-tests/pom.xml
@@ -47,7 +47,7 @@
com.tngtech.archunit
archunit-junit5
- 0.19.0
+ 1.3.0
diff --git a/backend/tests/syson-tests/src/main/java/org/eclipse/syson/tests/architecture/AbstractCodingRulesTests.java b/backend/tests/syson-tests/src/main/java/org/eclipse/syson/tests/architecture/AbstractCodingRulesTests.java
index 14924d860..bf40b4268 100644
--- a/backend/tests/syson-tests/src/main/java/org/eclipse/syson/tests/architecture/AbstractCodingRulesTests.java
+++ b/backend/tests/syson-tests/src/main/java/org/eclipse/syson/tests/architecture/AbstractCodingRulesTests.java
@@ -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
@@ -72,6 +72,8 @@ public abstract class AbstractCodingRulesTests {
private static final String GUAVA_THIRDPARTY = "com.google.thirdparty..";
+ private static final String ORG_ECLIPSE_CORE_RUNTIME = "org.eclipse.core.runtime..";
+
private static final String SPRING_STRINGUTILS = "org.springframework.util.StringUtils";
private static final String TESTCASE_SUFFIX = "TestCases";
@@ -101,7 +103,8 @@ public void noClassesShouldUseGraphQLGuava() {
.resideInAPackage(this.getProjectRootPackage())
.should()
.dependOnClassesThat()
- .resideInAnyPackage(GRAPHQL_GUAVA);
+ .resideInAnyPackage(GRAPHQL_GUAVA)
+ .allowEmptyShould(true);
rule.check(this.getClasses());
}
@@ -129,7 +132,21 @@ public void noClassesShouldUseGuava() {
GUAVA_UTIL,
GUAVA_XML,
GUAVA_THIRDPARTY
- );
+ )
+ .allowEmptyShould(true);
+
+ rule.check(this.getClasses());
+ }
+
+ @Test
+ public void noClassesShouldUseEclipseCoreRuntime() {
+ var rule = ArchRuleDefinition.noClasses()
+ .that()
+ .resideInAPackage(this.getProjectRootPackage())
+ .should()
+ .dependOnClassesThat()
+ .resideInAnyPackage(ORG_ECLIPSE_CORE_RUNTIME)
+ .allowEmptyShould(true);
rule.check(this.getClasses());
}
@@ -141,7 +158,8 @@ public void noClassesShouldUseJacksonAnnotations() {
.resideInAPackage(this.getProjectRootPackage())
.should()
.dependOnClassesThat()
- .resideInAPackage(JACKSON_ANNOTATION);
+ .resideInAPackage(JACKSON_ANNOTATION)
+ .allowEmptyShould(true);
rule.check(this.getClasses());
}
@@ -153,7 +171,8 @@ public void noClassesShouldUseJetbrainsImport() {
.resideInAPackage(this.getProjectRootPackage())
.should()
.dependOnClassesThat()
- .resideInAPackage(JETBRAINS);
+ .resideInAPackage(JETBRAINS)
+ .allowEmptyShould(true);
rule.check(this.getClasses());
}
@@ -167,7 +186,8 @@ public void noClassesShouldUseEMF() {
.resideInAPackage(EMF)
.orShould()
.dependOnClassesThat()
- .resideInAPackage(EMFJSON);
+ .resideInAPackage(EMFJSON)
+ .allowEmptyShould(true);
rule.check(this.getClasses());
}
@@ -181,7 +201,9 @@ public void noClassesShouldUseEcoreUtilDelete() {
.callMethod("org.eclipse.emf.ecore.util.EcoreUtil", "delete", "org.eclipse.emf.ecore.EObject")
.orShould()
.callMethod("org.eclipse.emf.ecore.util.EcoreUtil", "delete", "org.eclipse.emf.ecore.EObject", "boolean")
- .because("EcoreUtil.delete doesn't work well with Sysml Standard Libraries in the ResourceSet, use DeleteService instead");
+ .because("EcoreUtil.delete doesn't work well with Sysml Standard Libraries in the ResourceSet, use DeleteService instead")
+ .allowEmptyShould(true);
+
rule.check(this.getClasses());
}
@@ -191,7 +213,8 @@ public void noClassesShouldUseApacheCommons() {
.resideInAPackage(this.getProjectRootPackage())
.should()
.dependOnClassesThat()
- .resideInAPackage(APACHE_COMMONS);
+ .resideInAPackage(APACHE_COMMONS)
+ .allowEmptyShould(true);
rule.check(this.getClasses());
}
@@ -203,7 +226,8 @@ public void noClassesShouldUseSpringStringUtils() {
.resideInAPackage(this.getProjectRootPackage())
.should()
.dependOnClassesThat()
- .areAssignableTo(SPRING_STRINGUTILS);
+ .areAssignableTo(SPRING_STRINGUTILS)
+ .allowEmptyShould(true);
rule.check(this.getClasses());
}
@@ -213,7 +237,8 @@ public void noClassesShouldUseStandardStreams() {
ArchRule rule = ArchRuleDefinition.noClasses()
.that()
.resideInAPackage(this.getProjectRootPackage())
- .should(GeneralCodingRules.ACCESS_STANDARD_STREAMS);
+ .should(GeneralCodingRules.ACCESS_STANDARD_STREAMS)
+ .allowEmptyShould(true);
rule.check(this.getClasses());
}
@@ -223,7 +248,8 @@ public void noClassesShouldThrowGenericExceptions() {
ArchRule rule = ArchRuleDefinition.noClasses()
.that()
.resideInAPackage(this.getProjectRootPackage())
- .should(GeneralCodingRules.THROW_GENERIC_EXCEPTIONS);
+ .should(GeneralCodingRules.THROW_GENERIC_EXCEPTIONS)
+ .allowEmptyShould(true);
rule.check(this.getClasses());
}
@@ -233,7 +259,8 @@ public void noClassesShouldUseJavaLogging() {
ArchRule rule = ArchRuleDefinition.noClasses()
.that()
.resideInAPackage(this.getProjectRootPackage())
- .should(GeneralCodingRules.USE_JAVA_UTIL_LOGGING);
+ .should(GeneralCodingRules.USE_JAVA_UTIL_LOGGING)
+ .allowEmptyShould(true);
rule.check(this.getClasses());
}
@@ -245,7 +272,9 @@ public void classesShouldUsePublicVisibility() {
.resideInAPackage(this.getProjectRootPackage())
.should().bePackagePrivate()
.andShould().bePrivate()
- .andShould().beProtected();
+ .andShould().beProtected()
+ .allowEmptyShould(true);
+
rule.check(this.getClasses());
}
@@ -254,7 +283,9 @@ public void classesShouldHavePublicOrPrivateConstructors() {
ArchRule rule = ArchRuleDefinition.noClasses()
.that()
.resideInAPackage(this.getProjectRootPackage())
- .should(this.haveProtectedOrPackageConstructor());
+ .should(this.haveProtectedOrPackageConstructor())
+ .allowEmptyShould(true);
+
rule.check(this.getClasses());
}
@@ -294,7 +325,8 @@ public void interfacesShouldRespectNamingConventions() {
.and()
.areNotAnnotatedWith(Target.class)
.should()
- .haveSimpleNameStartingWith("I");
+ .haveSimpleNameStartingWith("I")
+ .allowEmptyShould(true);
rule.check(this.getClasses());
}
@@ -309,7 +341,8 @@ public void abstractClassesShouldRespectNamingConventions() {
.and()
.haveModifier(ABSTRACT)
.should()
- .haveSimpleNameStartingWith("Abstract");
+ .haveSimpleNameStartingWith("Abstract")
+ .allowEmptyShould(true);
rule.check(this.getClasses());
}
@@ -327,7 +360,8 @@ public void abstractClassesShouldNotContainBusinessCode() {
.areNotInterfaces()
.and()
.haveModifier(ABSTRACT)
- .should(this.notContainBusinessCode());
+ .should(this.notContainBusinessCode())
+ .allowEmptyShould(true);
rule.check(this.getClasses());
}
@@ -422,7 +456,8 @@ public void noMethodsShouldBeStatic() {
.and(this.isNotSwitchTable())
.and(this.isNotRecordStaticBuilder())
.should()
- .beStatic();
+ .beStatic()
+ .allowEmptyShould(true);
rule.check(this.getClasses());
}
@@ -430,7 +465,7 @@ public void noMethodsShouldBeStatic() {
private DescribedPredicate isNotTestCase() {
return new DescribedPredicate<>("is not a test case") {
@Override
- public boolean apply(JavaClass javaClass) {
+ public boolean test(JavaClass javaClass) {
return !javaClass.getName().endsWith(TESTCASE_SUFFIX);
}
};
@@ -444,7 +479,7 @@ public boolean apply(JavaClass javaClass) {
private DescribedPredicate isNotLambda() {
return new DescribedPredicate<>("is not a lambda") {
@Override
- public boolean apply(JavaMethod javaMethod) {
+ public boolean test(JavaMethod javaMethod) {
return !javaMethod.getName().startsWith("lambda$") && !javaMethod.getName().startsWith("$deserializeLambda$");
}
};
@@ -459,7 +494,7 @@ public boolean apply(JavaMethod javaMethod) {
private DescribedPredicate isNotSwitchTable() {
return new DescribedPredicate<>("is not a switch table (whatever that is...)") {
@Override
- public boolean apply(JavaMethod javaMethod) {
+ public boolean test(JavaMethod javaMethod) {
return !javaMethod.getFullName().contains("$SWITCH_TABLE$");
}
};
@@ -473,7 +508,7 @@ public boolean apply(JavaMethod javaMethod) {
private DescribedPredicate isNotRecordStaticBuilder() {
return new DescribedPredicate<>("is not an alternate builder in a record") {
@Override
- public boolean apply(JavaMethod javaMethod) {
+ public boolean test(JavaMethod javaMethod) {
return !(javaMethod.getOwner().isRecord() && javaMethod.getRawReturnType().equals(javaMethod.getOwner()));
}
};
diff --git a/backend/tests/syson-tests/src/main/java/org/eclipse/syson/tests/architecture/SysONServiceDependencyPredicate.java b/backend/tests/syson-tests/src/main/java/org/eclipse/syson/tests/architecture/SysONServiceDependencyPredicate.java
index 88296c4ab..15b899166 100644
--- a/backend/tests/syson-tests/src/main/java/org/eclipse/syson/tests/architecture/SysONServiceDependencyPredicate.java
+++ b/backend/tests/syson-tests/src/main/java/org/eclipse/syson/tests/architecture/SysONServiceDependencyPredicate.java
@@ -12,6 +12,7 @@
*******************************************************************************/
package org.eclipse.syson.tests.architecture;
+import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.core.domain.JavaClass;
import java.util.List;
@@ -22,7 +23,7 @@
*
* @author Arthur Daussy
*/
-public final class SysONServiceDependencyPredicate extends com.tngtech.archunit.base.DescribedPredicate {
+public final class SysONServiceDependencyPredicate extends DescribedPredicate {
private final String acceptableDependentServicePattern;
@@ -33,7 +34,7 @@ public SysONServiceDependencyPredicate(List authorizedScopes, String ope
}
@Override
- public boolean apply(JavaClass javaClass) {
+ public boolean test(JavaClass javaClass) {
String simpleName = javaClass.getSimpleName();
if (!simpleName.endsWith("Service") || !javaClass.getName().contains("syson")) {
diff --git a/doc/content/modules/user-manual/pages/release-notes/2026.3.0.adoc b/doc/content/modules/user-manual/pages/release-notes/2026.3.0.adoc
index 853d5e063..14671821f 100644
--- a/doc/content/modules/user-manual/pages/release-notes/2026.3.0.adoc
+++ b/doc/content/modules/user-manual/pages/release-notes/2026.3.0.adoc
@@ -23,6 +23,7 @@ It's not recommended for production use.
** Fix the textual export of `OccurrenceUsage` to avoid duplication of the _abstract_ keyword.
** Fix the textual export to properly escape names used in qualified names in some references.
+** Fix an error during textual export where `Expose` elements with apostrophes in their name were not properly escaped.
** Fix the textual import to properly resolve the _payload_ argument of a `SendActionUsage` used in a `TransitionUsage`.
The following model is now properly imported:
+