diff --git a/SysML2.NET.Tests/Extend/ViewUsageExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/ViewUsageExtensionsTestFixture.cs index 8f990dd47..b3d230674 100644 --- a/SysML2.NET.Tests/Extend/ViewUsageExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/ViewUsageExtensionsTestFixture.cs @@ -1,58 +1,229 @@ -// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- // -// +// // 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.Core.Types; + using SysML2.NET.Core.POCO.Kernel.Functions; + using SysML2.NET.Core.POCO.Kernel.Packages; + using SysML2.NET.Core.POCO.Root.Namespaces; + using SysML2.NET.Core.POCO.Systems.Requirements; using SysML2.NET.Core.POCO.Systems.Views; + using SysML2.NET.Extensions; [TestFixture] public class ViewUsageExtensionsTestFixture { [Test] - public void ComputeExposedElement_ThrowsNotSupportedException() + public void VerifyComputeExposedElement() { - Assert.That(() => ((IViewUsage)null).ComputeExposedElement(), Throws.TypeOf()); + Assert.That(() => ((IViewUsage)null).ComputeExposedElement(), Throws.TypeOf()); + + var viewUsage = new ViewUsage(); + + // Empty: no ownedImport → empty list. + Assert.That(viewUsage.ComputeExposedElement(), Is.Empty); + + // Discrimination: non-Expose import (MembershipImport) → excluded. + var nonExposeMembership = new MembershipImport(); + viewUsage.AssignOwnership(nonExposeMembership); + + 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()); } + [Test] - public void ComputeSatisfiedViewpoint_ThrowsNotSupportedException() + public void VerifyComputeSatisfiedViewpoint() { - Assert.That(() => ((IViewUsage)null).ComputeSatisfiedViewpoint(), Throws.TypeOf()); + Assert.That(() => ((IViewUsage)null).ComputeSatisfiedViewpoint(), Throws.TypeOf()); + + var viewUsage = new ViewUsage(); + + // Empty: no nestedRequirement → empty list. + Assert.That(viewUsage.ComputeSatisfiedViewpoint(), Is.Empty); + + // Discrimination: plain RequirementUsage (not ViewpointUsage) → excluded. + var plainRequirement = new RequirementUsage { IsComposite = true }; + var plainRequirementMembership = new FeatureMembership(); + viewUsage.AssignOwnership(plainRequirementMembership, plainRequirement); + + Assert.That(viewUsage.ComputeSatisfiedViewpoint(), Is.Empty); + + // Predicate discrimination: ViewpointUsage with IsComposite = false → excluded. + var nonCompositeViewpoint = new ViewpointUsage { IsComposite = false }; + var nonCompositeViewpointMembership = new FeatureMembership(); + viewUsage.AssignOwnership(nonCompositeViewpointMembership, nonCompositeViewpoint); + + Assert.That(viewUsage.ComputeSatisfiedViewpoint(), Is.Empty); + + // Positive: ViewpointUsage with IsComposite = true → returned. + var compositeViewpoint1 = new ViewpointUsage { IsComposite = true }; + var compositeViewpointMembership1 = new FeatureMembership(); + viewUsage.AssignOwnership(compositeViewpointMembership1, compositeViewpoint1); + + Assert.That(viewUsage.ComputeSatisfiedViewpoint(), Is.EqualTo([compositeViewpoint1])); + + // Populated: second composite ViewpointUsage also returned in iteration order. + var compositeViewpoint2 = new ViewpointUsage { IsComposite = true }; + var compositeViewpointMembership2 = new FeatureMembership(); + viewUsage.AssignOwnership(compositeViewpointMembership2, compositeViewpoint2); + + Assert.That(viewUsage.ComputeSatisfiedViewpoint(), Is.EqualTo([compositeViewpoint1, compositeViewpoint2])); + } + + [Test] + public void VerifyComputeViewCondition() + { + Assert.That(() => ((IViewUsage)null).ComputeViewCondition(), Throws.TypeOf()); + + var viewUsage = new ViewUsage(); + + // Empty: no ownedMembership → empty list. + Assert.That(viewUsage.ComputeViewCondition(), Is.Empty); + + // Discrimination: non-ElementFilterMembership in ownedMembership → excluded. + var nonFilterFeature = new Feature(); + var nonFilterMembership = new FeatureMembership(); + viewUsage.AssignOwnership(nonFilterMembership, nonFilterFeature); + + Assert.That(viewUsage.ComputeViewCondition(), Is.Empty); + + // STUB-BLOCKER: Wiring an ElementFilterMembership and reading its condition property + // dispatches to ElementFilterMembershipExtensions.ComputeCondition, which is a NotSupportedException + // stub. The populated case cannot be tested cleanly until that upstream stub is implemented. + var filterCondition = new BooleanExpression(); + var filterMembership = new ElementFilterMembership(); + viewUsage.AssignOwnership(filterMembership, filterCondition); + + Assert.That(() => viewUsage.ComputeViewCondition(), Throws.TypeOf()); } + [Test] - public void ComputeViewCondition_ThrowsNotSupportedException() + public void VerifyComputeViewDefinition() { - Assert.That(() => ((IViewUsage)null).ComputeViewCondition(), Throws.TypeOf()); + Assert.That(() => ((IViewUsage)null).ComputeViewDefinition(), Throws.TypeOf()); + + var viewUsage = new ViewUsage(); + + // Empty: no FeatureTyping → null. + Assert.That(viewUsage.ComputeViewDefinition(), Is.Null); + + // Negative: FeatureTyping whose Type is a non-ViewDefinition → null. + var nonViewDefinitionType = new Feature(); + var typingToNonViewDefinition = new FeatureTyping { Type = nonViewDefinitionType }; + viewUsage.AssignOwnership(typingToNonViewDefinition); + + Assert.That(viewUsage.ComputeViewDefinition(), Is.Null); + + // Positive: FeatureTyping whose Type is a ViewDefinition → returned. + var viewDefinition1 = new ViewDefinition(); + var typingToViewDefinition1 = new FeatureTyping { Type = viewDefinition1 }; + viewUsage.AssignOwnership(typingToViewDefinition1); + + Assert.That(viewUsage.ComputeViewDefinition(), Is.SameAs(viewDefinition1)); + + // Multiple: two ViewDefinition typings → first returned (FirstOrDefault). + var viewDefinition2 = new ViewDefinition(); + var typingToViewDefinition2 = new FeatureTyping { Type = viewDefinition2 }; + viewUsage.AssignOwnership(typingToViewDefinition2); + + Assert.That(viewUsage.ComputeViewDefinition(), Is.SameAs(viewDefinition1)); } + [Test] - public void ComputeViewDefinition_ThrowsNotSupportedException() + public void VerifyComputeViewRendering() { - Assert.That(() => ((IViewUsage)null).ComputeViewDefinition(), Throws.TypeOf()); + Assert.That(() => ((IViewUsage)null).ComputeViewRendering(), Throws.TypeOf()); + + var viewUsage = new ViewUsage(); + + // Empty: no featureMembership → null. + Assert.That(viewUsage.ComputeViewRendering(), Is.Null); + + // Discrimination: non-ViewRenderingMembership featureMembership → null. + var plainFeature = new Feature(); + var plainFeatureMembership = new FeatureMembership(); + viewUsage.AssignOwnership(plainFeatureMembership, plainFeature); + + Assert.That(viewUsage.ComputeViewRendering(), Is.Null); + + // STUB-BLOCKER: Wiring a ViewRenderingMembership and reading its referencedRendering + // property dispatches to ViewRenderingMembershipExtensions.ComputeReferencedRendering, + // which is a NotSupportedException stub. The populated positive case cannot be tested + // cleanly until that upstream stub is implemented. + var renderingUsage = new RenderingUsage(); + var viewRenderingMembership = new ViewRenderingMembership(); + viewUsage.AssignOwnership(viewRenderingMembership, renderingUsage); + + Assert.That(() => viewUsage.ComputeViewRendering(), Throws.TypeOf()); } + [Test] - public void ComputeViewRendering_ThrowsNotSupportedException() + public void VerifyComputeIncludeAsExposedOperation() { - Assert.That(() => ((IViewUsage)null).ComputeViewRendering(), Throws.TypeOf()); + // Null guard on subject. + Assert.That(() => ((IViewUsage)null).ComputeIncludeAsExposedOperation(new Feature()), Throws.TypeOf()); + + var viewUsage = new ViewUsage(); + + // Null guard on element parameter. + Assert.That(() => viewUsage.ComputeIncludeAsExposedOperation(null), Throws.TypeOf()); + + var element = new Feature(); + + // Case (a) — empty conditions: ViewUsage has no ElementFilterMembership in membership; + // forAll over empty sequence is vacuously true → returns true. + Assert.That(viewUsage.ComputeIncludeAsExposedOperation(element), Is.True); + + // Case (a) variant: non-filter memberships present, still no ElementFilterMembership → + // conditions remain empty → vacuously true. + var nonFilterFeature = new Feature(); + var nonFilterMembership = new FeatureMembership(); + viewUsage.AssignOwnership(nonFilterMembership, nonFilterFeature); + + Assert.That(viewUsage.ComputeIncludeAsExposedOperation(element), Is.True); + + // Cases (b), (c), (d) — STUB-BLOCKER: adding an ElementFilterMembership and reading its + // condition property dispatches to ElementFilterMembershipExtensions.ComputeCondition, + // which is a NotSupportedException stub. Additionally, annotation.annotatingElement + // dispatches to AnnotationExtensions.ComputeAnnotatingElement (also stubbed). These + // cases cannot be tested cleanly until the upstream stubs are implemented. + var filterCondition = new BooleanExpression(); + var filterMembership = new ElementFilterMembership(); + viewUsage.AssignOwnership(filterMembership, filterCondition); + + Assert.That(() => viewUsage.ComputeIncludeAsExposedOperation(element), Throws.TypeOf()); } } } diff --git a/SysML2.NET/Extend/ViewUsageExtensions.cs b/SysML2.NET/Extend/ViewUsageExtensions.cs index f8e71a04a..37f995e0f 100644 --- a/SysML2.NET/Extend/ViewUsageExtensions.cs +++ b/SysML2.NET/Extend/ViewUsageExtensions.cs @@ -1,4 +1,4 @@ -// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- // // // Copyright (C) 2022-2026 Starion Group S.A. @@ -22,40 +22,15 @@ namespace SysML2.NET.Core.POCO.Systems.Views { 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.Systems.Occurrences; - 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.Classes; using SysML2.NET.Core.POCO.Kernel.Functions; - using SysML2.NET.Core.POCO.Kernel.Structures; + using SysML2.NET.Core.POCO.Kernel.Metadata; + using SysML2.NET.Core.POCO.Kernel.Packages; 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.Requirements; - using SysML2.NET.Core.POCO.Systems.States; - using SysML2.NET.Core.POCO.Systems.UseCases; - using SysML2.NET.Core.POCO.Systems.VerificationCases; /// /// The class provides extensions methods for @@ -81,10 +56,19 @@ internal static class ViewUsageExtensions /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeExposedElement(this IViewUsage viewUsageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return viewUsageSubject == null + ? throw new ArgumentNullException(nameof(viewUsageSubject)) + : [ + ..viewUsageSubject.ownedImport + .OfType() + .SelectMany(expose => expose.ImportedMemberships([])) + .Select(membership => membership.MemberElement) + .Where(memberElement => memberElement != null) + .Where(viewUsageSubject.IncludeAsExposed) + .Distinct() + ]; } /// @@ -104,10 +88,11 @@ internal static List ComputeExposedElement(this IViewUsage viewUsageSu /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeSatisfiedViewpoint(this IViewUsage viewUsageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return viewUsageSubject == null + ? throw new ArgumentNullException(nameof(viewUsageSubject)) + : [..viewUsageSubject.nestedRequirement.OfType().Where(viewpointUsage => viewpointUsage.IsComposite)]; } /// @@ -127,10 +112,16 @@ internal static List ComputeSatisfiedViewpoint(this IViewUsage /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeViewCondition(this IViewUsage viewUsageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return viewUsageSubject == null + ? throw new ArgumentNullException(nameof(viewUsageSubject)) + : [ + ..viewUsageSubject.ownedMembership + .OfType() + .Select(elementFilterMembership => elementFilterMembership.condition) + .Where(condition => condition != null) + ]; } /// @@ -142,10 +133,15 @@ internal static List ComputeViewCondition(this IViewUsage viewUsage /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IViewDefinition ComputeViewDefinition(this IViewUsage viewUsageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return viewUsageSubject == null + ? throw new ArgumentNullException(nameof(viewUsageSubject)) + : viewUsageSubject.OwnedRelationship + .OfType() + .Select(featureTyping => featureTyping.Type) + .OfType() + .FirstOrDefault(); } /// @@ -168,10 +164,16 @@ internal static IViewDefinition ComputeViewDefinition(this IViewUsage viewUsageS /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IRenderingUsage ComputeViewRendering(this IViewUsage viewUsageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (viewUsageSubject == null) + { + throw new ArgumentNullException(nameof(viewUsageSubject)); + } + + var renderings = viewUsageSubject.featureMembership.OfType().ToList(); + + return renderings.Count == 0 ? null : renderings[0].referencedRendering; } /// @@ -198,10 +200,30 @@ internal static IRenderingUsage ComputeViewRendering(this IViewUsage viewUsageSu /// /// The expected /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static bool ComputeIncludeAsExposedOperation(this IViewUsage viewUsageSubject, IElement element) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (viewUsageSubject == null) + { + throw new ArgumentNullException(nameof(viewUsageSubject)); + } + + if (element == null) + { + throw new ArgumentNullException(nameof(element)); + } + + var metadataFeatures = element.ownedAnnotation + .Select(annotation => annotation.annotatingElement) + .OfType() + .ToList(); + + var conditions = viewUsageSubject.membership + .OfType() + .Select(elementFilterMembership => elementFilterMembership.condition) + .Where(condition => condition != null) + .ToList(); + + return conditions.All(condition => metadataFeatures.Any(condition.CheckCondition)); } } }