diff --git a/SysML2.NET.Tests/Extend/EndFeatureMembershipExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/EndFeatureMembershipExtensionsTestFixture.cs index 8a121303..90be40c4 100644 --- a/SysML2.NET.Tests/Extend/EndFeatureMembershipExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/EndFeatureMembershipExtensionsTestFixture.cs @@ -1,38 +1,88 @@ -// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- // -// +// // 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.Features; + using SysML2.NET.Core.POCO.Root.Elements; + using SysML2.NET.Core.POCO.Root.Namespaces; + using SysML2.NET.Exceptions; + using SysML2.NET.Extensions; + + using Type = SysML2.NET.Core.POCO.Core.Types.Type; [TestFixture] public class EndFeatureMembershipExtensionsTestFixture { [Test] - public void ComputeOwnedMemberFeature_ThrowsNotSupportedException() + public void VerifyComputeOwnedMemberFeature() { - Assert.That(() => ((IEndFeatureMembership)null).ComputeOwnedMemberFeature(), Throws.TypeOf()); + Assert.That(() => ((IEndFeatureMembership)null).ComputeOwnedMemberFeature(), Throws.TypeOf()); + + // Empty OwnedRelatedElement → [1..1] violation: throws IncompleteModelException. + var endFeatureMembership = new EndFeatureMembership(); + + Assert.That(() => endFeatureMembership.ComputeOwnedMemberFeature(), Throws.TypeOf()); + + // Single IFeature wired via the public API → returned. + var owningType = new Type(); + var feature = new Feature(); + + owningType.AssignOwnership(endFeatureMembership, feature); + + Assert.That(endFeatureMembership.ComputeOwnedMemberFeature(), Is.SameAs(feature)); + + // Two IFeatures in OwnedRelatedElement → [1..1] violation: throws IncompleteModelException. + var twoFeatureMembership = new EndFeatureMembership(); + var firstFeature = new Feature(); + var secondFeature = new Feature(); + + ((IContainedRelationship)twoFeatureMembership).OwnedRelatedElement.Add(firstFeature); + ((IContainedRelationship)twoFeatureMembership).OwnedRelatedElement.Add(secondFeature); + + Assert.That(() => twoFeatureMembership.ComputeOwnedMemberFeature(), Throws.TypeOf()); + + // Mixed-type owned related elements: exactly one IFeature alongside a non-IFeature (Namespace). + // The RequireSingleOfType projection MUST pick out the IFeature regardless of its position + // (this is the core robustness guarantee — never positionally index the unfiltered collection). + var mixedMembership = new EndFeatureMembership(); + var siblingNonFeature = new Namespace(); + var mixedFeature = new Feature(); + + ((IContainedRelationship)mixedMembership).OwnedRelatedElement.Add(siblingNonFeature); + ((IContainedRelationship)mixedMembership).OwnedRelatedElement.Add(mixedFeature); + + Assert.That(mixedMembership.ComputeOwnedMemberFeature(), Is.SameAs(mixedFeature)); + + // OwnedRelatedElement populated with non-IFeature element(s) only → no IFeature match: + // [1..1] violation, throws IncompleteModelException. + var nonFeatureMembership = new EndFeatureMembership(); + var nonFeatureElement = new Namespace(); + + ((IContainedRelationship)nonFeatureMembership).OwnedRelatedElement.Add(nonFeatureElement); + + Assert.That(() => nonFeatureMembership.ComputeOwnedMemberFeature(), Throws.TypeOf()); } } } diff --git a/SysML2.NET/Extend/EndFeatureMembershipExtensions.cs b/SysML2.NET/Extend/EndFeatureMembershipExtensions.cs index 538a5844..1e5ef201 100644 --- a/SysML2.NET/Extend/EndFeatureMembershipExtensions.cs +++ b/SysML2.NET/Extend/EndFeatureMembershipExtensions.cs @@ -21,13 +21,8 @@ namespace SysML2.NET.Core.POCO.Core.Features { using System; - using System.Collections.Generic; - using SysML2.NET.Core.Root.Namespaces; - using SysML2.NET.Core.POCO.Core.Types; - using SysML2.NET.Core.POCO.Root.Annotations; - using SysML2.NET.Core.POCO.Root.Elements; - using SysML2.NET.Core.POCO.Root.Namespaces; + using SysML2.NET.Extensions; /// /// The class provides extensions methods for @@ -44,11 +39,14 @@ internal static class EndFeatureMembershipExtensions /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IFeature ComputeOwnedMemberFeature(this IEndFeatureMembership endFeatureMembershipSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); - } + if (endFeatureMembershipSubject == null) + { + throw new ArgumentNullException(nameof(endFeatureMembershipSubject)); + } + return endFeatureMembershipSubject.OwnedRelatedElement.RequireSingleOfType(nameof(endFeatureMembershipSubject)); + } } }