diff --git a/emv2/org.osate.aadl2.errormodel.tests/models/issue2832/.gitignore b/emv2/org.osate.aadl2.errormodel.tests/models/issue2832/.gitignore new file mode 100644 index 0000000000..afce51184c --- /dev/null +++ b/emv2/org.osate.aadl2.errormodel.tests/models/issue2832/.gitignore @@ -0,0 +1,2 @@ +/.aadlbin-gen/ +/instances/ diff --git a/emv2/org.osate.aadl2.errormodel.tests/models/issue2832/.project b/emv2/org.osate.aadl2.errormodel.tests/models/issue2832/.project new file mode 100644 index 0000000000..c30a2078c0 --- /dev/null +++ b/emv2/org.osate.aadl2.errormodel.tests/models/issue2832/.project @@ -0,0 +1,18 @@ + + + issue2832 + + + + + + org.eclipse.xtext.ui.shared.xtextBuilder + + + + + + org.osate.core.aadlnature + org.eclipse.xtext.ui.shared.xtextNature + + diff --git a/emv2/org.osate.aadl2.errormodel.tests/models/issue2832/Issue2832.aadl b/emv2/org.osate.aadl2.errormodel.tests/models/issue2832/Issue2832.aadl new file mode 100644 index 0000000000..70a782b57d --- /dev/null +++ b/emv2/org.osate.aadl2.errormodel.tests/models/issue2832/Issue2832.aadl @@ -0,0 +1,36 @@ +package Issue2832 +public + system s + modes + m1: initial mode; + m2: mode; + annex EMV2 {** + use behavior Issue2832::machine; + component error behavior + mode mappings + state1 in modes (m1); + end component; + **} in modes (m2); + end s; + + system mixed + modes + m1: initial mode; + m2: mode; + m3: mode; + annex EMV2 {** + use behavior Issue2832::machine; + component error behavior + mode mappings + state1 in modes (m1, m2, m3); + end component; + **} in modes (m2); + end mixed; + + annex EMV2 {** + error behavior machine + states + state1: initial state; + end behavior; + **}; +end Issue2832; diff --git a/emv2/org.osate.aadl2.errormodel.tests/models/issue2832/UnresolvedMode.aadl b/emv2/org.osate.aadl2.errormodel.tests/models/issue2832/UnresolvedMode.aadl new file mode 100644 index 0000000000..2a5e9d169c --- /dev/null +++ b/emv2/org.osate.aadl2.errormodel.tests/models/issue2832/UnresolvedMode.aadl @@ -0,0 +1,22 @@ +package UnresolvedMode +public + system s + modes + m1: initial mode; + m2: mode; + annex EMV2 {** + use behavior UnresolvedMode::machine; + component error behavior + mode mappings + state1 in modes (missing); + end component; + **} in modes (m2); + end s; + + annex EMV2 {** + error behavior machine + states + state1: initial state; + end behavior; + **}; +end UnresolvedMode; diff --git a/emv2/org.osate.aadl2.errormodel.tests/models/issue2832/ValidModeMappings.aadl b/emv2/org.osate.aadl2.errormodel.tests/models/issue2832/ValidModeMappings.aadl new file mode 100644 index 0000000000..db09f14c23 --- /dev/null +++ b/emv2/org.osate.aadl2.errormodel.tests/models/issue2832/ValidModeMappings.aadl @@ -0,0 +1,54 @@ +package ValidModeMappings +public + system unrestricted + modes + m1: initial mode; + m2: mode; + m3: mode; + annex EMV2 {** + use behavior ValidModeMappings::machine; + component error behavior + mode mappings + state1 in modes (m1, m2, m3); + end component; + **}; + end unrestricted; + + system restricted + modes + m1: initial mode; + m2: mode; + m3: mode; + annex EMV2 {** + use behavior ValidModeMappings::machine; + component error behavior + mode mappings + state1 in modes (m2); + state1 in modes (m2, m3); + end component; + **} in modes (m2, m3); + end restricted; + + system parent + modes + m1: initial mode; + m2: mode; + end parent; + + system child extends parent + annex EMV2 {** + use behavior ValidModeMappings::machine; + component error behavior + mode mappings + state1 in modes (m2); + end component; + **} in modes (m2); + end child; + + annex EMV2 {** + error behavior machine + states + state1: initial state; + end behavior; + **}; +end ValidModeMappings; diff --git a/emv2/org.osate.aadl2.errormodel.tests/src/org/osate/aadl2/errormodel/tests/issues/Issue2832Test.java b/emv2/org.osate.aadl2.errormodel.tests/src/org/osate/aadl2/errormodel/tests/issues/Issue2832Test.java new file mode 100644 index 0000000000..1684994726 --- /dev/null +++ b/emv2/org.osate.aadl2.errormodel.tests/src/org/osate/aadl2/errormodel/tests/issues/Issue2832Test.java @@ -0,0 +1,103 @@ +/** + * Copyright (c) 2004-2026 Carnegie Mellon University and others. (see Contributors file). + * All Rights Reserved. + * + * NO WARRANTY. ALL MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY + * KIND, EITHER EXPRESSED OR IMPLIED, AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR PURPOSE + * OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT + * MAKE ANY WARRANTY OF ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT INFRINGEMENT. + * + * This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * SPDX-License-Identifier: EPL-2.0 + * + * Created, in part, with funding and support from the United States Government. (see Acknowledgments file). + * + * This program includes and/or can make use of certain third party source code, object code, documentation and other + * files ("Third Party Software"). The Third Party Software that is used by this program is dependent upon your system + * configuration. By using this program, You agree to comply with any and all relevant Third Party Software terms and + * conditions contained in any such Third Party Software or separate license file distributed with such Third Party + * Software. The parties who own the Third Party Software ("Third Party Licensors") are intended third party benefici- + * aries to this license with respect to the terms applicable to their Third Party Software. Third Party Software li- + * censes only apply to the Third Party Software and not any other portion of this program or this program as a whole. + */ +package org.osate.aadl2.errormodel.tests.issues; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.xtext.resource.XtextResource; +import org.eclipse.xtext.testing.InjectWith; +import org.eclipse.xtext.testing.XtextRunner; +import org.eclipse.xtext.testing.validation.ValidationTestHelper; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.osate.aadl2.AadlPackage; +import org.osate.aadl2.Classifier; +import org.osate.aadl2.DefaultAnnexSubclause; +import org.osate.aadl2.errormodel.tests.ErrorModelInjectorProvider; +import org.osate.testsupport.AssertHelper; +import org.osate.testsupport.TestHelper; +import org.osate.xtext.aadl2.errormodel.errorModel.ErrorModelSubclause; +import org.osate.xtext.aadl2.errormodel.errorModel.ErrorStateToModeMapping; + +import com.google.inject.Inject; +import com.itemis.xtext.testing.FluentIssueCollection; +import com.itemis.xtext.testing.XtextTest; + +@RunWith(XtextRunner.class) +@InjectWith(ErrorModelInjectorProvider.class) +public class Issue2832Test extends XtextTest { + private static final String PROJECT_LOCATION = "org.osate.aadl2.errormodel.tests/models/issue2832/"; + + @Inject + private TestHelper testHelper; + + @Inject + private ValidationTestHelper validationHelper; + + @Test + public void rejectModesOutsideTheModalSubclause() throws Exception { + var result = issues = testHelper.testFile(PROJECT_LOCATION + "Issue2832.aadl"); + var expected = new FluentIssueCollection(result.getResource(), new ArrayList<>(), new ArrayList<>()); + var pkg = (AadlPackage) result.getResource().getContents().getFirst(); + var classifiers = pkg.getPublicSection().getOwnedClassifiers(); + AssertHelper.assertError(mapping(classifiers.get(0)), result.getIssues(), expected, + "Mapped mode m1 must be listed in the enclosing EMV2 subclause's in modes"); + AssertHelper.assertError(mapping(classifiers.get(1)), result.getIssues(), expected, + "Mapped mode m1 must be listed in the enclosing EMV2 subclause's in modes", + "Mapped mode m3 must be listed in the enclosing EMV2 subclause's in modes"); + expected.sizeIs(3); + assertConstraints(expected); + + var source = ((XtextResource) result.getResource()).getParseResult().getRootNode().getText(); + var markedModes = result.getIssues().stream() + .map(issue -> source.substring(issue.getOffset(), issue.getOffset() + issue.getLength())).sorted() + .toList(); + assertEquals(List.of("m1", "m1", "m3"), markedModes); + } + + @Test + public void acceptNonmodalSubclausesAndAllowedInheritedModes() { + validationHelper.assertNoIssues(testHelper.parseFile(PROJECT_LOCATION + "ValidModeMappings.aadl")); + } + + @Test + public void unresolvedModesOnlyReportTheLinkingError() throws Exception { + var result = issues = testHelper.testFile(PROJECT_LOCATION + "UnresolvedMode.aadl"); + var expected = new FluentIssueCollection(result.getResource(), new ArrayList<>(), new ArrayList<>()); + var pkg = (AadlPackage) result.getResource().getContents().getFirst(); + AssertHelper.assertError(mapping(pkg.getPublicSection().getOwnedClassifiers().getFirst()), result.getIssues(), + expected, "Couldn't resolve reference to mode 'missing'."); + expected.sizeIs(1); + assertConstraints(expected); + } + + private ErrorStateToModeMapping mapping(Classifier classifier) { + var annex = (ErrorModelSubclause) ((DefaultAnnexSubclause) classifier.getOwnedAnnexSubclauses().getFirst()) + .getParsedAnnexSubclause(); + return annex.getErrorStateToModeMappings().getFirst(); + } +} diff --git a/emv2/org.osate.xtext.aadl2.errormodel/src/org/osate/xtext/aadl2/errormodel/validation/ErrorModelValidator.java b/emv2/org.osate.xtext.aadl2.errormodel/src/org/osate/xtext/aadl2/errormodel/validation/ErrorModelValidator.java index b8acd26436..a20e552751 100644 --- a/emv2/org.osate.xtext.aadl2.errormodel/src/org/osate/xtext/aadl2/errormodel/validation/ErrorModelValidator.java +++ b/emv2/org.osate.xtext.aadl2.errormodel/src/org/osate/xtext/aadl2/errormodel/validation/ErrorModelValidator.java @@ -334,6 +334,7 @@ public void caseErrorModelSubclause(ErrorModelSubclause subclause) { checkTypePropagationAndContainment(errorPropagation); } checkUseBehavior(subclause); + checkModeMappings(subclause); } @Check(CheckType.FAST) @@ -433,6 +434,24 @@ private void checkEventTriggerType(ErrorBehaviorEvent event, List } } + private void checkModeMappings(ErrorModelSubclause subclause) { + var annex = EcoreUtil2.getContainerOfType(subclause, DefaultAnnexSubclause.class); + if (annex == null || annex.getInModes().isEmpty()) { + return; + } + for (var mapping : subclause.getErrorStateToModeMappings()) { + var modes = mapping.getMappedModes(); + for (int i = 0; i < modes.size(); i++) { + var mode = modes.get(i); + if (!mode.eIsProxy() && !annex.getInModes().contains(mode)) { + error("Mapped mode " + mode.getName() + + " must be listed in the enclosing EMV2 subclause's in modes", mapping, + ErrorModelPackage.Literals.ERROR_STATE_TO_MODE_MAPPING__MAPPED_MODES, i, null); + } + } + } + } + private void checkUseBehavior(ErrorModelSubclause subclause) { // now find it in use behavior clause EList emslist = EMV2Util.getAllContainingClassifierEMV2Subclauses(subclause);