diff --git a/SysML2.NET.Tests/Extend/ImportExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/ImportExtensionsTestFixture.cs
index b3e794bb..8b1fceba 100644
--- a/SysML2.NET.Tests/Extend/ImportExtensionsTestFixture.cs
+++ b/SysML2.NET.Tests/Extend/ImportExtensionsTestFixture.cs
@@ -1,44 +1,124 @@
-// -------------------------------------------------------------------------------------------------
+// -------------------------------------------------------------------------------------------------
//
-//
+//
// 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 Moq;
+
using NUnit.Framework;
-
+
using SysML2.NET.Core.POCO.Root.Namespaces;
+ using SysML2.NET.Extensions;
+
+ using IElement = SysML2.NET.Core.POCO.Root.Elements.IElement;
[TestFixture]
public class ImportExtensionsTestFixture
{
[Test]
- public void ComputeImportedElement_ThrowsNotSupportedException()
+ public void VerifyComputeImportedElement()
{
- Assert.That(() => ((IImport)null).ComputeImportedElement(), Throws.TypeOf());
+ Assert.That(() => ((IImport)null).ComputeImportedElement(), Throws.TypeOf());
+
+ // Wildcard arm: an IImport that is neither an INamespaceImport nor an IMembershipImport
+ // returns null. Use Moq because IImport is abstract and has no concrete non-subtype POCO.
+ var abstractImport = new Mock().Object;
+
+ Assert.That(abstractImport.ComputeImportedElement(), Is.Null);
+
+ // INamespaceImport arm: returns the ImportedNamespace.
+ var importedNamespace = new Namespace();
+ var namespaceImport = new NamespaceImport { ImportedNamespace = importedNamespace };
+
+ Assert.That(namespaceImport.ComputeImportedElement(), Is.SameAs(importedNamespace));
+
+ // INamespaceImport with no ImportedNamespace set → null.
+ var emptyNamespaceImport = new NamespaceImport();
+
+ Assert.That(emptyNamespaceImport.ComputeImportedElement(), Is.Null);
+
+ // IMembershipImport arm: returns the ImportedMembership.MemberElement.
+ var ownerNamespace = new Namespace();
+ var memberElement = new Namespace();
+ var importedMembership = new OwningMembership();
+ ownerNamespace.AssignOwnership(importedMembership, memberElement);
+
+ var membershipImport = new MembershipImport { ImportedMembership = importedMembership };
+
+ Assert.That(membershipImport.ComputeImportedElement(), Is.SameAs(memberElement));
+
+ // IMembershipImport with no ImportedMembership set → null (null-conditional propagates).
+ var emptyMembershipImport = new MembershipImport();
+
+ Assert.That(emptyMembershipImport.ComputeImportedElement(), Is.Null);
+
+ // IMembershipImport whose ImportedMembership has a null MemberElement → null.
+ // A real OwningMembership with zero OwnedRelatedElement throws IncompleteModelException
+ // from ComputeOwnedMemberElement's invariant guard, so use a Moq'd IMembership to
+ // produce a null MemberElement directly without violating the model invariant.
+ var nullMemberMembership = new Mock();
+ nullMemberMembership.SetupGet(membership => membership.MemberElement).Returns((IElement)null);
+ var membershipImportNoMember = new MembershipImport { ImportedMembership = nullMemberMembership.Object };
+
+ Assert.That(membershipImportNoMember.ComputeImportedElement(), Is.Null);
}
-
+
[Test]
- public void ComputeImportOwningNamespace_ThrowsArgumentNullException()
+ public void VerifyComputeImportOwningNamespace()
{
Assert.That(() => ((IImport)null).ComputeImportOwningNamespace(), Throws.TypeOf());
+
+ // No owner wired → null.
+ var orphanImport = new NamespaceImport();
+
+ Assert.That(orphanImport.ComputeImportOwningNamespace(), Is.Null);
+
+ // Owner wired via AssignOwnership → returns the owning namespace.
+ var owningNamespace = new Namespace();
+ var ownedImport = new NamespaceImport();
+ owningNamespace.AssignOwnership(ownedImport);
+
+ Assert.That(ownedImport.ComputeImportOwningNamespace(), Is.SameAs(owningNamespace));
+ }
+
+ [Test]
+ public void VerifyComputeImportedMembershipsOperation()
+ {
+ // Import.importedMemberships is abstract in the UML; both concrete subclasses
+ // (NamespaceImport, MembershipImport) redefine it, and their POCO partials route
+ // IImport.ImportedMemberships(...) directly to ComputeRedefinedImportedMembershipsOperation
+ // on the matching subtype. This static extension on the abstract base is therefore
+ // unreachable at runtime and is documented as a deliberate NotSupportedException guard.
+ Assert.That(
+ () => ((IImport)null).ComputeImportedMembershipsOperation([]),
+ Throws.TypeOf());
+
+ Assert.That(
+ () => new NamespaceImport().ComputeImportedMembershipsOperation([]),
+ Throws.TypeOf());
+
+ Assert.That(
+ () => new MembershipImport().ComputeImportedMembershipsOperation([]),
+ Throws.TypeOf());
}
}
}
diff --git a/SysML2.NET/Extend/ImportExtensions.cs b/SysML2.NET/Extend/ImportExtensions.cs
index c75a1829..40a3de45 100644
--- a/SysML2.NET/Extend/ImportExtensions.cs
+++ b/SysML2.NET/Extend/ImportExtensions.cs
@@ -36,16 +36,33 @@ internal static class ImportExtensions
///
/// Computes the derived property.
///
+ ///
+ /// Spec-text-only: the UML Import.importedElement attribute has no OCL body of its
+ /// own. Each concrete subclass defines its own derivation rule
+ /// (deriveMembershipImportImportedElement: importedElement = importedMembership.memberElement,
+ /// and deriveNamespaceImportImportedElement: importedElement = importedNamespace).
+ /// Both subtype POCOs route their importedElement getter through this single static
+ /// extension, so the dispatch happens here via a switch on the subject's concrete type.
+ ///
///
/// The subject
///
///
/// the computed result
///
- [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal static IElement ComputeImportedElement(this IImport importSubject)
{
- throw new NotSupportedException("Create a GitHub issue when this method is required");
+ if (importSubject == null)
+ {
+ throw new ArgumentNullException(nameof(importSubject));
+ }
+
+ return importSubject switch
+ {
+ IMembershipImport membershipImport => membershipImport.ImportedMembership?.MemberElement,
+ INamespaceImport namespaceImport => namespaceImport.ImportedNamespace,
+ _ => null
+ };
}
///
@@ -76,15 +93,22 @@ internal static INamespace ComputeImportOwningNamespace(this IImport importSubje
/// The expected collection of
///
///
- /// This method is no longer called at runtime. The POCO classes (,
- /// ) now provide explicit interface implementations of
- /// that dispatch directly to their respective
- /// ComputeRedefinedImportedMembershipsOperation extension methods.
+ /// The UML Import.importedMemberships operation is abstract (no body in the XMI). Both
+ /// concrete subclasses, and , redefine
+ /// it with their own OCL bodyConditions, and their POCO partials provide explicit-interface
+ /// implementations of that dispatch directly to their
+ /// respective ComputeRedefinedImportedMembershipsOperation extension methods. Consequently,
+ /// this static extension on the abstract base is never reached at runtime, and a deliberate
+ /// guards any future direct call.
///
[System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal static List ComputeImportedMembershipsOperation(this IImport importSubject, List excluded)
{
- throw new NotSupportedException("This method should not be called. Import subtypes handle dispatch via explicit interface implementations.");
+ throw new NotSupportedException(
+ "Import is abstract and its importedMemberships operation is redefined by every concrete subclass " +
+ "(NamespaceImport, MembershipImport). The POCO partials route IImport.ImportedMemberships(...) " +
+ "directly to ComputeRedefinedImportedMembershipsOperation on the matching subtype, so this static " +
+ "extension is unreachable at runtime.");
}
}
}