diff --git a/SysML2.NET.Tests/Extend/LibraryPackageExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/LibraryPackageExtensionsTestFixture.cs
new file mode 100644
index 00000000..2c1ede38
--- /dev/null
+++ b/SysML2.NET.Tests/Extend/LibraryPackageExtensionsTestFixture.cs
@@ -0,0 +1,43 @@
+// -------------------------------------------------------------------------------------------------
+//
+//
+// 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.Kernel.Packages;
+
+ [TestFixture]
+ public class LibraryPackageExtensionsTestFixture
+ {
+ [Test]
+ public void VerifyComputeRedefinedLibraryNamespaceOperation()
+ {
+ Assert.That(() => ((ILibraryPackage)null).ComputeRedefinedLibraryNamespaceOperation(), Throws.TypeOf());
+
+ // The libraryNamespace of a LibraryPackage is itself (OCL: self) -> reference identity.
+ var subject = new LibraryPackage();
+
+ Assert.That(subject.ComputeRedefinedLibraryNamespaceOperation(), Is.SameAs(subject));
+ }
+ }
+}
diff --git a/SysML2.NET.Tests/Extend/MembershipImportExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/MembershipImportExtensionsTestFixture.cs
new file mode 100644
index 00000000..427ab63a
--- /dev/null
+++ b/SysML2.NET.Tests/Extend/MembershipImportExtensionsTestFixture.cs
@@ -0,0 +1,96 @@
+// -------------------------------------------------------------------------------------------------
+//
+//
+// 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.Root.Namespaces;
+ using SysML2.NET.Core.POCO.Root.Annotations;
+ using SysML2.NET.Core.POCO.Root.Namespaces;
+ using SysML2.NET.Extensions;
+
+ [TestFixture]
+ public class MembershipImportExtensionsTestFixture
+ {
+ [Test]
+ public void VerifyComputeRedefinedImportedMembershipsOperation()
+ {
+ Assert.That(() => ((IMembershipImport)null).ComputeRedefinedImportedMembershipsOperation([]), Throws.TypeOf());
+
+ // Not recursive: importedElement IS a Namespace but recursion is suppressed -> [importedMembership] only.
+ var notRecursiveMemberNamespace = new Namespace();
+ var notRecursiveImportedMembership = new Membership { MemberElement = notRecursiveMemberNamespace };
+ var notRecursiveSubject = new MembershipImport { ImportedMembership = notRecursiveImportedMembership, IsRecursive = false };
+
+ var notRecursiveResult = notRecursiveSubject.ComputeRedefinedImportedMembershipsOperation([]);
+
+ using (Assert.EnterMultipleScope())
+ {
+ Assert.That(notRecursiveResult, Has.Count.EqualTo(1));
+ Assert.That(notRecursiveResult, Does.Contain(notRecursiveImportedMembership));
+ }
+
+ // Recursive, but importedElement is NOT a Namespace (a Comment) -> [importedMembership] only.
+ var nonNamespaceImportedMembership = new Membership { MemberElement = new Comment() };
+ var nonNamespaceSubject = new MembershipImport { ImportedMembership = nonNamespaceImportedMembership, IsRecursive = true };
+
+ var nonNamespaceResult = nonNamespaceSubject.ComputeRedefinedImportedMembershipsOperation([]);
+
+ using (Assert.EnterMultipleScope())
+ {
+ Assert.That(nonNamespaceResult, Has.Count.EqualTo(1));
+ Assert.That(nonNamespaceResult, Does.Contain(nonNamespaceImportedMembership));
+ }
+
+ // Recursive + importedElement IS a Namespace but that Namespace is in excluded -> [importedMembership] only.
+ var excludedNamespace = new Namespace();
+ var excludedImportedMembership = new Membership { MemberElement = excludedNamespace };
+ var excludedSubject = new MembershipImport { ImportedMembership = excludedImportedMembership, IsRecursive = true };
+
+ var excludedResult = excludedSubject.ComputeRedefinedImportedMembershipsOperation([excludedNamespace]);
+
+ using (Assert.EnterMultipleScope())
+ {
+ Assert.That(excludedResult, Has.Count.EqualTo(1));
+ Assert.That(excludedResult, Does.Contain(excludedImportedMembership));
+ }
+
+ // Recursive + importedElement IS a Namespace with a public visible membership, not excluded ->
+ // importedMembership FIRST, then the imported namespace's visibleMemberships appended.
+ var importedNamespace = new Namespace();
+ var visibleMembership = new OwningMembership { Visibility = VisibilityKind.Public };
+ importedNamespace.AssignOwnership(visibleMembership, new Namespace());
+
+ var recursiveImportedMembership = new Membership { MemberElement = importedNamespace };
+ var recursiveSubject = new MembershipImport { ImportedMembership = recursiveImportedMembership, IsRecursive = true, IsImportAll = false };
+
+ var recursiveResult = recursiveSubject.ComputeRedefinedImportedMembershipsOperation([]);
+
+ using (Assert.EnterMultipleScope())
+ {
+ Assert.That(recursiveResult[0], Is.SameAs(recursiveImportedMembership));
+ Assert.That(recursiveResult, Does.Contain(visibleMembership));
+ }
+ }
+ }
+}
diff --git a/SysML2.NET.Tests/Extend/PackageExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/PackageExtensionsTestFixture.cs
index 58f4da0f..29691563 100644
--- a/SysML2.NET.Tests/Extend/PackageExtensionsTestFixture.cs
+++ b/SysML2.NET.Tests/Extend/PackageExtensionsTestFixture.cs
@@ -89,15 +89,23 @@ public void VerifyComputeRedefinedImportedMembershipsOperation()
Assert.That(package.ComputeRedefinedImportedMembershipsOperation([]), Is.Empty);
- var importMember = new MembershipImport();
+ // Valid MembershipImport (ImportedMembership wired to a real membership) with no
+ // filterConditions: MembershipImport.importedMemberships is non-recursive, so it yields
+ // [importedMembership]; Package.importedMemberships aggregates it and, absent any
+ // ElementFilterMembership, ComputeRedefinedImportedMembershipsOperation returns it as-is.
+ var importedMemberNamespace = new Namespace();
+ var importedMembership = new Membership { MemberElement = importedMemberNamespace };
+ var importMember = new MembershipImport { ImportedMembership = importedMembership, IsRecursive = false };
package.AssignOwnership(importMember);
- Assert.That(() => package.ComputeRedefinedImportedMembershipsOperation([]), Throws.InstanceOf());
+ Assert.That(package.ComputeRedefinedImportedMembershipsOperation([]), Does.Contain(importedMembership));
+ // Discrimination: a bare BooleanExpression filterCondition evaluates to [] → CheckCondition
+ // returns false → the imported membership fails the filter and is excluded → empty result.
var membership = new ElementFilterMembership();
var expression = new BooleanExpression();
package.AssignOwnership(membership, expression);
- Assert.That(() => package.ComputeRedefinedImportedMembershipsOperation([]), Throws.InstanceOf());
+ Assert.That(package.ComputeRedefinedImportedMembershipsOperation([]), Is.Empty);
}
}
}
diff --git a/SysML2.NET.Tests/Extend/ViewUsageExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/ViewUsageExtensionsTestFixture.cs
index 5245d61d..058695f7 100644
--- a/SysML2.NET.Tests/Extend/ViewUsageExtensionsTestFixture.cs
+++ b/SysML2.NET.Tests/Extend/ViewUsageExtensionsTestFixture.cs
@@ -54,15 +54,19 @@ public void VerifyComputeExposedElement()
Assert.That(viewUsage.ComputeExposedElement(), Is.Empty);
- // STUB-BLOCKER: adding a MembershipExpose dispatches ComputeExposedElement to
- // MembershipExpose.ImportedMemberships(excluded), which calls
- // MembershipImportExtensions.ComputeRedefinedImportedMembershipsOperation — a
- // NotSupportedException stub. The populated positive case cannot be tested until that
- // upstream stub is implemented.
- var membershipExpose = new MembershipExpose();
- viewUsage.AssignOwnership(membershipExpose);
-
- Assert.That(viewUsage.ComputeExposedElement, Throws.TypeOf());
+ // Positive: a valid MembershipExpose (ImportedMembership wired to a real membership whose
+ // memberElement is exposable) routes through the now-implemented
+ // MembershipImport.importedMemberships. Non-recursive → [importedMembership]; its
+ // memberElement surfaces and, with no ElementFilterMembership conditions, passes
+ // includeAsExposed → the exposed element is returned. Uses a fresh ViewUsage so the
+ // positive path is not contaminated by the deliberately-minimal discrimination import above.
+ var positiveViewUsage = new ViewUsage();
+ var exposedElement = new Feature();
+ var exposeImportedMembership = new Membership { MemberElement = exposedElement };
+ var membershipExpose = new MembershipExpose { ImportedMembership = exposeImportedMembership, IsRecursive = false };
+ positiveViewUsage.AssignOwnership(membershipExpose);
+
+ Assert.That(positiveViewUsage.ComputeExposedElement(), Does.Contain(exposedElement));
}
[Test]
diff --git a/SysML2.NET/Extend/LibraryPackageExtensions.cs b/SysML2.NET/Extend/LibraryPackageExtensions.cs
index b8c6323f..e3c39e59 100644
--- a/SysML2.NET/Extend/LibraryPackageExtensions.cs
+++ b/SysML2.NET/Extend/LibraryPackageExtensions.cs
@@ -50,10 +50,14 @@ internal static class LibraryPackageExtensions
///
/// The expected
///
- [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal static INamespace ComputeRedefinedLibraryNamespaceOperation(this ILibraryPackage libraryPackageSubject)
{
- throw new NotSupportedException("Create a GitHub issue when this method is required");
+ if (libraryPackageSubject == null)
+ {
+ throw new ArgumentNullException(nameof(libraryPackageSubject));
+ }
+
+ return libraryPackageSubject;
}
}
}
diff --git a/SysML2.NET/Extend/MembershipImportExtensions.cs b/SysML2.NET/Extend/MembershipImportExtensions.cs
index f4a9a52a..151a19d2 100644
--- a/SysML2.NET/Extend/MembershipImportExtensions.cs
+++ b/SysML2.NET/Extend/MembershipImportExtensions.cs
@@ -60,10 +60,23 @@ internal static class MembershipImportExtensions
///
/// The expected collection of
///
- [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage]
internal static List ComputeRedefinedImportedMembershipsOperation(this IMembershipImport membershipImportSubject, List excluded)
{
- throw new NotSupportedException("Create a GitHub issue when this method is required");
+ if (membershipImportSubject == null)
+ {
+ throw new ArgumentNullException(nameof(membershipImportSubject));
+ }
+
+ if (!membershipImportSubject.IsRecursive
+ || membershipImportSubject.importedElement is not INamespace importedNamespace
+ || excluded.Contains(importedNamespace))
+ {
+ return [membershipImportSubject.ImportedMembership];
+ }
+
+ var visibleMemberships = importedNamespace.VisibleMemberships(excluded, true, membershipImportSubject.IsImportAll);
+
+ return [membershipImportSubject.ImportedMembership, .. visibleMemberships];
}
}
}