From e30b16136ac4e6b7312d52d3dfee0c6f6ec8cc0a Mon Sep 17 00:00:00 2001 From: atheate Date: Thu, 21 May 2026 16:02:16 +0200 Subject: [PATCH] Fix #173 --- ...uirementDefinitionExtensionsTestFixture.cs | 215 +++++++++++++++--- .../Extend/RequirementDefinitionExtensions.cs | 83 ++++--- 2 files changed, 230 insertions(+), 68 deletions(-) diff --git a/SysML2.NET.Tests/Extend/RequirementDefinitionExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/RequirementDefinitionExtensionsTestFixture.cs index bb6e6cd9..2af5cd12 100644 --- a/SysML2.NET.Tests/Extend/RequirementDefinitionExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/RequirementDefinitionExtensionsTestFixture.cs @@ -1,73 +1,236 @@ // ------------------------------------------------------------------------------------------------- // -// +// // Copyright 2022-2026 Starion Group S.A. -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -// +// // // ------------------------------------------------------------------------------------------------ namespace SysML2.NET.Tests.Extend { using System; - + using NUnit.Framework; - + + using SysML2.NET.Core.POCO.Core.Types; + using SysML2.NET.Core.POCO.Kernel.Behaviors; + using SysML2.NET.Core.POCO.Root.Annotations; + using SysML2.NET.Core.POCO.Systems.Constraints; + using SysML2.NET.Core.POCO.Systems.DefinitionAndUsage; + using SysML2.NET.Core.POCO.Systems.Parts; using SysML2.NET.Core.POCO.Systems.Requirements; + using SysML2.NET.Core.Systems.Requirements; + using SysML2.NET.Extensions; [TestFixture] public class RequirementDefinitionExtensionsTestFixture { [Test] - public void ComputeActorParameter_ThrowsNotSupportedException() + public void VerifyComputeActorParameter() { - Assert.That(() => ((IRequirementDefinition)null).ComputeActorParameter(), Throws.TypeOf()); + Assert.That(() => ((IRequirementDefinition)null).ComputeActorParameter(), Throws.TypeOf()); + + var requirementDefinition = new RequirementDefinition(); + + Assert.That(requirementDefinition.ComputeActorParameter(), Is.Empty); + + // Discrimination: add a ParameterMembership (not ActorMembership) — must be excluded from result. + var parameterMembership = new ParameterMembership(); + var parameterUsage = new Usage(); + requirementDefinition.AssignOwnership(parameterMembership, parameterUsage); + + Assert.That(requirementDefinition.ComputeActorParameter(), Is.Empty); + + // Populated case: ActorMembership is present; selecting ownedActorParameter triggers an + // upstream stub (ActorMembershipExtensions.ComputeOwnedActorParameter is not yet implemented). + var actorMembership = new ActorMembership(); + var actorPartUsage = new PartUsage(); + requirementDefinition.AssignOwnership(actorMembership, actorPartUsage); + + Assert.That(() => requirementDefinition.ComputeActorParameter(), Throws.TypeOf()); } - + [Test] - public void ComputeAssumedConstraint_ThrowsNotSupportedException() + public void VerifyComputeAssumedConstraint() { - Assert.That(() => ((IRequirementDefinition)null).ComputeAssumedConstraint(), Throws.TypeOf()); + Assert.That(() => ((IRequirementDefinition)null).ComputeAssumedConstraint(), Throws.TypeOf()); + + var requirementDefinition = new RequirementDefinition(); + + Assert.That(requirementDefinition.ComputeAssumedConstraint(), Is.Empty); + + // Type discrimination: add a FeatureMembership (not IRequirementConstraintMembership) — excluded. + var featureMembership = new FeatureMembership(); + var featureUsage = new Usage(); + requirementDefinition.AssignOwnership(featureMembership, featureUsage); + + Assert.That(requirementDefinition.ComputeAssumedConstraint(), Is.Empty); + + // Kind discrimination: add a RequirementConstraintMembership with Kind = Requirement — excluded. + var requiredMembership = new RequirementConstraintMembership { Kind = RequirementConstraintKind.Requirement }; + var requiredConstraintUsage = new ConstraintUsage(); + requirementDefinition.AssignOwnership(requiredMembership, requiredConstraintUsage); + + Assert.That(requirementDefinition.ComputeAssumedConstraint(), Is.Empty); + + // Populated case: RequirementConstraintMembership with Kind = Assumption; selecting + // ownedConstraint triggers an upstream stub (RequirementConstraintMembershipExtensions + // .ComputeOwnedConstraint is not yet implemented). + var assumedMembership = new RequirementConstraintMembership { Kind = RequirementConstraintKind.Assumption }; + var assumedConstraintUsage = new ConstraintUsage(); + requirementDefinition.AssignOwnership(assumedMembership, assumedConstraintUsage); + + Assert.That(() => requirementDefinition.ComputeAssumedConstraint(), Throws.TypeOf()); } - + [Test] - public void ComputeFramedConcern_ThrowsNotSupportedException() + public void VerifyComputeFramedConcern() { - Assert.That(() => ((IRequirementDefinition)null).ComputeFramedConcern(), Throws.TypeOf()); + Assert.That(() => ((IRequirementDefinition)null).ComputeFramedConcern(), Throws.TypeOf()); + + var requirementDefinition = new RequirementDefinition(); + + Assert.That(requirementDefinition.ComputeFramedConcern(), Is.Empty); + + // Discrimination cannot be tested at this layer: any IFeatureMembership subtype (including + // RequirementConstraintMembership) causes subject.featureMembership to traverse + // RemoveRedefinedFeatures → IFeatureMembership.ownedMemberFeature, which dispatches to the + // stubbed RequirementConstraintMembershipExtensions.ComputeOwnedConstraint and throws + // NotSupportedException — identical to the populated stub-blocker case below. + // The discrimination block is omitted until upstream stubs are implemented. + + // Populated case: FramedConcernMembership is present; selecting ownedConcern triggers an + // upstream stub (FramedConcernMembershipExtensions.ComputeOwnedConcern is not yet implemented). + var framedMembership = new FramedConcernMembership(); + var concernUsage = new ConcernUsage(); + requirementDefinition.AssignOwnership(framedMembership, concernUsage); + + Assert.That(() => requirementDefinition.ComputeFramedConcern(), Throws.TypeOf()); } + [Test] - public void ComputeRequiredConstraint_ThrowsNotSupportedException() + public void VerifyComputeRequiredConstraint() { - Assert.That(() => ((IRequirementDefinition)null).ComputeRequiredConstraint(), Throws.TypeOf()); + Assert.That(() => ((IRequirementDefinition)null).ComputeRequiredConstraint(), Throws.TypeOf()); + + var requirementDefinition = new RequirementDefinition(); + + Assert.That(requirementDefinition.ComputeRequiredConstraint(), Is.Empty); + + // Type discrimination: add a FeatureMembership (not IRequirementConstraintMembership) — excluded. + var featureMembership = new FeatureMembership(); + var featureUsage = new Usage(); + requirementDefinition.AssignOwnership(featureMembership, featureUsage); + + Assert.That(requirementDefinition.ComputeRequiredConstraint(), Is.Empty); + + // Kind discrimination: add a RequirementConstraintMembership with Kind = Assumption — excluded. + var assumedMembership = new RequirementConstraintMembership { Kind = RequirementConstraintKind.Assumption }; + var assumedConstraintUsage = new ConstraintUsage(); + requirementDefinition.AssignOwnership(assumedMembership, assumedConstraintUsage); + + Assert.That(requirementDefinition.ComputeRequiredConstraint(), Is.Empty); + + // Populated case: RequirementConstraintMembership with Kind = Requirement; selecting + // ownedConstraint triggers an upstream stub (RequirementConstraintMembershipExtensions + // .ComputeOwnedConstraint is not yet implemented). + var requiredMembership = new RequirementConstraintMembership { Kind = RequirementConstraintKind.Requirement }; + var requiredConstraintUsage = new ConstraintUsage(); + requirementDefinition.AssignOwnership(requiredMembership, requiredConstraintUsage); + + Assert.That(() => requirementDefinition.ComputeRequiredConstraint(), Throws.TypeOf()); } - + [Test] - public void ComputeStakeholderParameter_ThrowsNotSupportedException() + public void VerifyComputeStakeholderParameter() { - Assert.That(() => ((IRequirementDefinition)null).ComputeStakeholderParameter(), Throws.TypeOf()); + Assert.That(() => ((IRequirementDefinition)null).ComputeStakeholderParameter(), Throws.TypeOf()); + + var requirementDefinition = new RequirementDefinition(); + + Assert.That(requirementDefinition.ComputeStakeholderParameter(), Is.Empty); + + // Discrimination cannot be tested at this layer: any IFeatureMembership subtype (including + // ActorMembership) causes subject.featureMembership to traverse RemoveRedefinedFeatures → + // IFeatureMembership.ownedMemberFeature, which dispatches to the stubbed + // ActorMembershipExtensions.ComputeOwnedActorParameter and throws NotSupportedException — + // identical to the populated stub-blocker case below. + // The discrimination block is omitted until upstream stubs are implemented. + + // Populated case: StakeholderMembership is present; selecting ownedStakeholderParameter + // triggers an upstream stub (StakeholderMembershipExtensions.ComputeOwnedStakeholderParameter + // is not yet implemented). + var stakeholderMembership = new StakeholderMembership(); + var stakeholderPartUsage = new PartUsage(); + requirementDefinition.AssignOwnership(stakeholderMembership, stakeholderPartUsage); + + Assert.That(() => requirementDefinition.ComputeStakeholderParameter(), Throws.TypeOf()); } - + [Test] - public void ComputeSubjectParameter_ThrowsNotSupportedException() + public void VerifyComputeSubjectParameter() { - Assert.That(() => ((IRequirementDefinition)null).ComputeSubjectParameter(), Throws.TypeOf()); + Assert.That(() => ((IRequirementDefinition)null).ComputeSubjectParameter(), Throws.TypeOf()); + + var requirementDefinition = new RequirementDefinition(); + + // Empty case: no SubjectMembership in featureMembership → null. + Assert.That(requirementDefinition.ComputeSubjectParameter(), Is.Null); + + // Discrimination: add a ParameterMembership (not SubjectMembership) → still null. + var parameterMembership = new ParameterMembership(); + var parameterUsage = new Usage(); + requirementDefinition.AssignOwnership(parameterMembership, parameterUsage); + + Assert.That(requirementDefinition.ComputeSubjectParameter(), Is.Null); + + // Populated case: SubjectMembership is present; selecting ownedSubjectParameter triggers an + // upstream stub (SubjectMembershipExtensions.ComputeOwnedSubjectParameter is not yet implemented). + var subjectMembership = new SubjectMembership(); + var subjectUsage = new Usage(); + requirementDefinition.AssignOwnership(subjectMembership, subjectUsage); + + Assert.That(() => requirementDefinition.ComputeSubjectParameter(), Throws.TypeOf()); } - + + private static readonly string[] ExpectedSingleComputedText = ["The requirement text."]; + private static readonly string[] ExpectedMultipleComputedText = ["The requirement text.", "Additional context."]; + [Test] - public void ComputeText_ThrowsNotSupportedException() + public void VerifyComputeText() { - Assert.That(() => ((IRequirementDefinition)null).ComputeText(), Throws.TypeOf()); + Assert.That(() => ((IRequirementDefinition)null).ComputeText(), Throws.TypeOf()); + + var requirementDefinition = new RequirementDefinition(); + + // Empty case: no Documentation elements → empty list. + Assert.That(requirementDefinition.ComputeText(), Is.Empty); + + // One Documentation with a body. + var firstDocumentation = new Documentation { Body = "The requirement text." }; + var firstAnnotation = new Annotation(); + requirementDefinition.AssignOwnership(firstAnnotation, firstDocumentation); + + Assert.That(requirementDefinition.ComputeText(), Is.EqualTo(ExpectedSingleComputedText)); + + // Two Documentation elements — both bodies appear in order. + var secondDocumentation = new Documentation { Body = "Additional context." }; + var secondAnnotation = new Annotation(); + requirementDefinition.AssignOwnership(secondAnnotation, secondDocumentation); + + Assert.That(requirementDefinition.ComputeText(), Is.EqualTo(ExpectedMultipleComputedText)); } } } diff --git a/SysML2.NET/Extend/RequirementDefinitionExtensions.cs b/SysML2.NET/Extend/RequirementDefinitionExtensions.cs index 5f6bfda1..49dcd615 100644 --- a/SysML2.NET/Extend/RequirementDefinitionExtensions.cs +++ b/SysML2.NET/Extend/RequirementDefinitionExtensions.cs @@ -22,38 +22,13 @@ namespace SysML2.NET.Core.POCO.Systems.Requirements { using System; using System.Collections.Generic; + using System.Linq; - using SysML2.NET.Core.Core.Types; - using SysML2.NET.Core.Root.Namespaces; - using SysML2.NET.Core.POCO.Core.Classifiers; - using SysML2.NET.Core.POCO.Core.Features; - using SysML2.NET.Core.POCO.Core.Types; - using SysML2.NET.Core.POCO.Kernel.Behaviors; - using SysML2.NET.Core.POCO.Kernel.Functions; + using SysML2.NET.Core.Systems.Requirements; using SysML2.NET.Core.POCO.Root.Annotations; - using SysML2.NET.Core.POCO.Root.Elements; - using SysML2.NET.Core.POCO.Root.Namespaces; - using SysML2.NET.Core.POCO.Systems.Actions; - using SysML2.NET.Core.POCO.Systems.Allocations; - using SysML2.NET.Core.POCO.Systems.AnalysisCases; - using SysML2.NET.Core.POCO.Systems.Attributes; - using SysML2.NET.Core.POCO.Systems.Calculations; - using SysML2.NET.Core.POCO.Systems.Cases; - using SysML2.NET.Core.POCO.Systems.Connections; using SysML2.NET.Core.POCO.Systems.Constraints; using SysML2.NET.Core.POCO.Systems.DefinitionAndUsage; - using SysML2.NET.Core.POCO.Systems.Enumerations; - using SysML2.NET.Core.POCO.Systems.Flows; - using SysML2.NET.Core.POCO.Systems.Interfaces; - using SysML2.NET.Core.POCO.Systems.Items; - using SysML2.NET.Core.POCO.Systems.Metadata; - using SysML2.NET.Core.POCO.Systems.Occurrences; using SysML2.NET.Core.POCO.Systems.Parts; - using SysML2.NET.Core.POCO.Systems.Ports; - using SysML2.NET.Core.POCO.Systems.States; - using SysML2.NET.Core.POCO.Systems.UseCases; - using SysML2.NET.Core.POCO.Systems.VerificationCases; - using SysML2.NET.Core.POCO.Systems.Views; /// /// The class provides extensions methods for @@ -78,10 +53,11 @@ internal static class RequirementDefinitionExtensions /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeActorParameter(this IRequirementDefinition requirementDefinitionSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return requirementDefinitionSubject == null + ? throw new ArgumentNullException(nameof(requirementDefinitionSubject)) + : [..requirementDefinitionSubject.featureMembership.OfType().Select(actorMembership => actorMembership.ownedActorParameter)]; } /// @@ -102,10 +78,16 @@ internal static List ComputeActorParameter(this IRequirementDefiniti /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeAssumedConstraint(this IRequirementDefinition requirementDefinitionSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return requirementDefinitionSubject == null + ? throw new ArgumentNullException(nameof(requirementDefinitionSubject)) + : [ + ..requirementDefinitionSubject.ownedFeatureMembership + .OfType() + .Where(requirementConstraintMembership => requirementConstraintMembership.Kind == RequirementConstraintKind.Assumption) + .Select(requirementConstraintMembership => requirementConstraintMembership.ownedConstraint) + ]; } /// @@ -125,10 +107,11 @@ internal static List ComputeAssumedConstraint(this IRequiremen /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeFramedConcern(this IRequirementDefinition requirementDefinitionSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return requirementDefinitionSubject == null + ? throw new ArgumentNullException(nameof(requirementDefinitionSubject)) + : [..requirementDefinitionSubject.featureMembership.OfType().Select(framedConcernMembership => framedConcernMembership.ownedConcern)]; } /// @@ -149,10 +132,16 @@ internal static List ComputeFramedConcern(this IRequirementDefini /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeRequiredConstraint(this IRequirementDefinition requirementDefinitionSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return requirementDefinitionSubject == null + ? throw new ArgumentNullException(nameof(requirementDefinitionSubject)) + : [ + ..requirementDefinitionSubject.ownedFeatureMembership + .OfType() + .Where(requirementConstraintMembership => requirementConstraintMembership.Kind == RequirementConstraintKind.Requirement) + .Select(requirementConstraintMembership => requirementConstraintMembership.ownedConstraint) + ]; } /// @@ -172,10 +161,12 @@ internal static List ComputeRequiredConstraint(this IRequireme /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeStakeholderParameter(this IRequirementDefinition requirementDefinitionSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + // The OCL uses "StakholderMembership" which is a typo in the XMI source; the correct C# type is IStakeholderMembership. + return requirementDefinitionSubject == null + ? throw new ArgumentNullException(nameof(requirementDefinitionSubject)) + : [..requirementDefinitionSubject.featureMembership.OfType().Select(stakeholderMembership => stakeholderMembership.ownedStakeholderParameter)]; } /// @@ -198,10 +189,18 @@ internal static List ComputeStakeholderParameter(this IRequirementDe /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IUsage ComputeSubjectParameter(this IRequirementDefinition requirementDefinitionSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (requirementDefinitionSubject == null) + { + throw new ArgumentNullException(nameof(requirementDefinitionSubject)); + } + + var subjects = requirementDefinitionSubject.featureMembership.OfType().ToList(); + + return subjects.Count == 0 + ? null + : subjects[0].ownedSubjectParameter; } /// @@ -219,11 +218,11 @@ internal static IUsage ComputeSubjectParameter(this IRequirementDefinition requi /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeText(this IRequirementDefinition requirementDefinitionSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return requirementDefinitionSubject == null + ? throw new ArgumentNullException(nameof(requirementDefinitionSubject)) + : [..requirementDefinitionSubject.documentation.Select(documentation => documentation.Body)]; } - } }