From 34bb63d2097bf3dc6c4f4b9efd5e2c6255f96e3d Mon Sep 17 00:00:00 2001 From: Pierre-Charles David Date: Wed, 21 Jan 2026 09:40:17 +0100 Subject: [PATCH] [1876] Fix potential NPE deleting some elements from Explorer view Bug: https://github.com/eclipse-syson/syson/issues/1876 Signed-off-by: Pierre-Charles David --- CHANGELOG.adoc | 1 + .../syson/services/RelatedElementsSwitch.java | 5 +++++ .../syson/services/DeleteServiceTest.java | 22 ++++++++++++++++++- .../e2e/diagram_node_creation.spec.ts | 4 +++- scripts/check-coverage.jsh | 2 +- 5 files changed, 31 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 783f6d285..badb99860 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -54,6 +54,7 @@ This fix ensure that imported models containing, for example, a top-level `Libra - https://github.com/eclipse-syson/syson/issues/1852[#1852] [diagrams] Do not propose the _Duplicate Element_ node action on elements which do not support it. - https://github.com/eclipse-syson/syson/issues/1857[#1857] [diagrams] Fix an issue where existing graphical node could disappear when new graphical node was created. - https://github.com/eclipse-syson/syson/issues/1865[#1865] [explorer] Fix focus loss on some characters in the _New object from text_ modal. +- https://github.com/eclipse-syson/syson/issues/1876[#1876] [explorer] Fix potential NPE when trying to delete elements with associated `EAnnotations` from the _Explorer_. === Improvements diff --git a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/RelatedElementsSwitch.java b/backend/services/syson-services/src/main/java/org/eclipse/syson/services/RelatedElementsSwitch.java index 54c72047e..06bfd8b2f 100644 --- a/backend/services/syson-services/src/main/java/org/eclipse/syson/services/RelatedElementsSwitch.java +++ b/backend/services/syson-services/src/main/java/org/eclipse/syson/services/RelatedElementsSwitch.java @@ -57,6 +57,11 @@ public Set caseElement(Element object) { return Set.of(); } + @Override + public Set defaultCase(EObject object) { + return Set.of(); + } + @Override public Set caseAnnotation(Annotation object) { Set relatedElements = new HashSet<>(); diff --git a/backend/services/syson-services/src/test/java/org/eclipse/syson/services/DeleteServiceTest.java b/backend/services/syson-services/src/test/java/org/eclipse/syson/services/DeleteServiceTest.java index 7848fbf41..7a714f85a 100644 --- a/backend/services/syson-services/src/test/java/org/eclipse/syson/services/DeleteServiceTest.java +++ b/backend/services/syson-services/src/test/java/org/eclipse/syson/services/DeleteServiceTest.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 @@ -15,9 +15,12 @@ import static org.assertj.core.api.Assertions.assertThat; import org.eclipse.emf.common.util.URI; +import org.eclipse.emf.ecore.EAnnotation; +import org.eclipse.emf.ecore.EcoreFactory; import org.eclipse.emf.ecore.impl.EPackageRegistryImpl; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; +import org.eclipse.emf.ecore.util.ECrossReferenceAdapter; import org.eclipse.sirius.emfjson.resource.JsonResourceFactoryImpl; import org.eclipse.syson.sysml.SysmlFactory; import org.eclipse.syson.sysml.SysmlPackage; @@ -98,4 +101,21 @@ void testMembershipExpose() { assertThat(view1.getExposedElement()).isEmpty(); } + + @DisplayName("GIVEN a SysML element with and EMF EAnnotation, WHEN it is deleted, THEN the deletion completes without error") + @Test + void testDeleteSysMLElementWithEAnnotation() { + var pkg1 = SysmlFactory.eINSTANCE.createPackage(); + pkg1.setDeclaredName("Pkg1"); + this.resource.getContents().add(pkg1); + + EAnnotation annotation = EcoreFactory.eINSTANCE.createEAnnotation(); + pkg1.getEAnnotations().add(annotation); + + this.resource.getResourceSet().eAdapters().add(new ECrossReferenceAdapter()); + + assertThat(this.resource.getContents()).hasSize(1).contains(pkg1); + this.deleteService.deleteFromModel(pkg1); + assertThat(this.resource.getContents()).isEmpty(); + } } diff --git a/integration-tests-playwright/playwright/e2e/diagram_node_creation.spec.ts b/integration-tests-playwright/playwright/e2e/diagram_node_creation.spec.ts index 1778fe436..aad28d51f 100644 --- a/integration-tests-playwright/playwright/e2e/diagram_node_creation.spec.ts +++ b/integration-tests-playwright/playwright/e2e/diagram_node_creation.spec.ts @@ -41,7 +41,7 @@ test.describe('diagram - general view', () => { await page.getByTestId('tool-New Part Definition').click(); diagram.expectNumberOfTopNodes(1); // Create Port - const partDefinitionNode = new PlaywrightNode(page, 'PartDefinition1', 'List'); + let partDefinitionNode = new PlaywrightNode(page, 'PartDefinition1', 'List'); await expect(partDefinitionNode.nodeLocator).toBeAttached(); await partDefinitionNode.openPalette(); await page.getByTestId('toolSection-Structure').click(); @@ -56,6 +56,8 @@ test.describe('diagram - general view', () => { await expect(portNode.nodeLocator).toBeAttached(); diagram.expectNumberOfTopNodes(3); // The port can be displayed as a list item + partDefinitionNode = new PlaywrightNode(page, 'PartDefinition1', 'List'); + await expect(partDefinitionNode.nodeLocator).toBeAttached(); await partDefinitionNode.click(); await partDefinitionNode.revealElement('ports'); const portsListNode = new PlaywrightNode(page, 'ports', 'List'); diff --git a/scripts/check-coverage.jsh b/scripts/check-coverage.jsh index 8592e5318..9326a8963 100755 --- a/scripts/check-coverage.jsh +++ b/scripts/check-coverage.jsh @@ -53,7 +53,7 @@ var moduleCoverageData = List.of( new ModuleCoverage("syson-sysml-validation", 99.0), new ModuleCoverage("syson-table-requirements-view", 77.0), new ModuleCoverage("syson-table-services", 100.0), - new ModuleCoverage("syson-tree-explorer-view", 88.0), + new ModuleCoverage("syson-tree-explorer-view", 87.0), new ModuleCoverage("syson-tree-services", 82.0) );