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]; } } }