diff --git a/CLAUDE.md b/CLAUDE.md index 18c5b0f5..3fcd2f47 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -142,6 +142,7 @@ Auto-generated DTOs use structured namespaces reflecting the KerML/SysML package ## Key Conventions +- **Paths are ALWAYS repo-relative — NEVER absolute.** This rule applies to every path the agent writes anywhere: code comments, XML doc `` and prose, source-string citations, error/log messages, commit messages, PR bodies, GitHub issue bodies, `.team-notes/` spec files, plan files, skill prompts and agent briefs (e.g. say `SysML2.NET/Extend/FooExtensions.cs`, NOT `C:\CODE\SysML2.NET\SysML2.NET\Extend\FooExtensions.cs` and NOT `/c/CODE/SysML2.NET/...`). Use forward slashes. Reason: absolute paths are user-/machine-specific and leak the local filesystem into the repo and into communication with other contributors — they break for anyone else, get stale on rename/move, and are noisy. The ONLY exception is the `Read` / `Edit` / `Write` tool `file_path` parameter, which the tool implementation requires to be absolute — those tool arguments are not user-visible artifacts. Everything you author as content must be repo-relative. - Commit messages use prefix tags: `[Add]`, `[Update]`, `[Remove]`, `[Fix]` — except for issue-fixing commits produced by `/implement-extensions` and `/implement-extensions-batch`, which use the canonical short form `Fix #` (single issue) or `Fix # # …` (batch) so GitHub auto-closes the issues on merge. - Main branch: `master`. Development branch: `development`. **All feature work targets `development`** via PR; `master` is downstream only. - CI: GitHub Actions (`CodeQuality.yml`) — builds, tests, and runs SonarQube analysis diff --git a/SysML2.NET.Tests/Extend/RenderingDefinitionExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/RenderingDefinitionExtensionsTestFixture.cs index dba4cacc..4ce2c853 100644 --- a/SysML2.NET.Tests/Extend/RenderingDefinitionExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/RenderingDefinitionExtensionsTestFixture.cs @@ -1,38 +1,64 @@ -// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- // -// +// // 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.Types; + using SysML2.NET.Core.POCO.Systems.Parts; using SysML2.NET.Core.POCO.Systems.Views; + using SysML2.NET.Extensions; [TestFixture] public class RenderingDefinitionExtensionsTestFixture { [Test] - public void ComputeRendering_ThrowsNotSupportedException() + public void VerifyComputeRendering() { - Assert.That(() => ((IRenderingDefinition)null).ComputeRendering(), Throws.TypeOf()); + Assert.That(() => ((IRenderingDefinition)null).ComputeRendering(), Throws.TypeOf()); + + var renderingDefinition = new RenderingDefinition(); + + // Empty: no usages wired → empty result. + Assert.That(renderingDefinition.ComputeRendering(), Is.Empty); + + // Discrimination: PartUsage in usage (not IRenderingUsage) → excluded. + var partUsage = new PartUsage(); + renderingDefinition.AssignOwnership(new FeatureMembership(), partUsage); + + Assert.That(renderingDefinition.ComputeRendering(), Is.Empty); + + // Positive: RenderingUsage wired via FeatureMembership → appears in usage → included. + var renderingUsage = new RenderingUsage(); + renderingDefinition.AssignOwnership(new FeatureMembership(), renderingUsage); + + Assert.That(renderingDefinition.ComputeRendering(), Is.EqualTo([renderingUsage])); + + // Multiple: second RenderingUsage also returned. + var renderingUsage2 = new RenderingUsage(); + renderingDefinition.AssignOwnership(new FeatureMembership(), renderingUsage2); + + Assert.That(renderingDefinition.ComputeRendering(), Is.EqualTo([renderingUsage, renderingUsage2])); } } } diff --git a/SysML2.NET.Tests/Extend/RenderingUsageExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/RenderingUsageExtensionsTestFixture.cs index 08c0a25c..2576441b 100644 --- a/SysML2.NET.Tests/Extend/RenderingUsageExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/RenderingUsageExtensionsTestFixture.cs @@ -1,38 +1,67 @@ -// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- // -// +// // 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.Systems.Parts; using SysML2.NET.Core.POCO.Systems.Views; + using SysML2.NET.Extensions; [TestFixture] public class RenderingUsageExtensionsTestFixture { [Test] - public void ComputeRenderingDefinition_ThrowsNotSupportedException() + public void VerifyComputeRenderingDefinition() { - Assert.That(() => ((IRenderingUsage)null).ComputeRenderingDefinition(), Throws.TypeOf()); + Assert.That(() => ((IRenderingUsage)null).ComputeRenderingDefinition(), Throws.TypeOf()); + + var renderingUsage = new RenderingUsage(); + + // Empty: no OwnedRelationship → null. + Assert.That(renderingUsage.ComputeRenderingDefinition(), Is.Null); + + // Negative: FeatureTyping whose Type is a non-RenderingDefinition (PartDefinition) → null. + var partDefinition = new PartDefinition(); + var typingToPartDefinition = new FeatureTyping { Type = partDefinition }; + renderingUsage.AssignOwnership(typingToPartDefinition); + + Assert.That(renderingUsage.ComputeRenderingDefinition(), Is.Null); + + // Positive: FeatureTyping whose Type is a RenderingDefinition → returned. + var renderingDefinition = new RenderingDefinition(); + var typingToRenderingDefinition = new FeatureTyping { Type = renderingDefinition }; + renderingUsage.AssignOwnership(typingToRenderingDefinition); + + Assert.That(renderingUsage.ComputeRenderingDefinition(), Is.SameAs(renderingDefinition)); + + // Multiple: second RenderingDefinition typing present; FirstOrDefault returns the first match. + var renderingDefinition2 = new RenderingDefinition(); + var typingToRenderingDefinition2 = new FeatureTyping { Type = renderingDefinition2 }; + renderingUsage.AssignOwnership(typingToRenderingDefinition2); + + Assert.That(renderingUsage.ComputeRenderingDefinition(), Is.SameAs(renderingDefinition)); } } } diff --git a/SysML2.NET.Tests/Extend/ViewDefinitionExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/ViewDefinitionExtensionsTestFixture.cs index cf64bd5f..9ba97979 100644 --- a/SysML2.NET.Tests/Extend/ViewDefinitionExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/ViewDefinitionExtensionsTestFixture.cs @@ -1,56 +1,162 @@ -// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- // -// +// // 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.Parts; + using SysML2.NET.Core.POCO.Systems.Requirements; using SysML2.NET.Core.POCO.Systems.Views; + using SysML2.NET.Extensions; [TestFixture] public class ViewDefinitionExtensionsTestFixture { [Test] - public void ComputeSatisfiedViewpoint_ThrowsNotSupportedException() + public void VerifyComputeSatisfiedViewpoint() { - Assert.That(() => ((IViewDefinition)null).ComputeSatisfiedViewpoint(), Throws.TypeOf()); + Assert.That(() => ((IViewDefinition)null).ComputeSatisfiedViewpoint(), Throws.TypeOf()); + + var viewDefinition = new ViewDefinition(); + + // Empty: no ownedRequirement → empty result. + Assert.That(viewDefinition.ComputeSatisfiedViewpoint(), Is.Empty); + + // Discrimination: plain RequirementUsage (not IViewpointUsage) → excluded. + var plainRequirement = new RequirementUsage { IsComposite = true }; + viewDefinition.AssignOwnership(new FeatureMembership(), plainRequirement); + + Assert.That(viewDefinition.ComputeSatisfiedViewpoint(), Is.Empty); + + // Predicate discrimination: ViewpointUsage with IsComposite = false → excluded. + var nonCompositeViewpoint = new ViewpointUsage { IsComposite = false }; + viewDefinition.AssignOwnership(new FeatureMembership(), nonCompositeViewpoint); + + Assert.That(viewDefinition.ComputeSatisfiedViewpoint(), Is.Empty); + + // Positive: composite ViewpointUsage → included. + var compositeViewpoint = new ViewpointUsage { IsComposite = true }; + viewDefinition.AssignOwnership(new FeatureMembership(), compositeViewpoint); + + Assert.That(viewDefinition.ComputeSatisfiedViewpoint(), Is.EqualTo([compositeViewpoint])); + + // Multiple: second composite ViewpointUsage also returned in iteration order. + var compositeViewpoint2 = new ViewpointUsage { IsComposite = true }; + viewDefinition.AssignOwnership(new FeatureMembership(), compositeViewpoint2); + + Assert.That(viewDefinition.ComputeSatisfiedViewpoint(), Is.EqualTo([compositeViewpoint, compositeViewpoint2])); } - + [Test] - public void ComputeView_ThrowsNotSupportedException() + public void VerifyComputeView() { - Assert.That(() => ((IViewDefinition)null).ComputeView(), Throws.TypeOf()); + Assert.That(() => ((IViewDefinition)null).ComputeView(), Throws.TypeOf()); + + var viewDefinition = new ViewDefinition(); + + // Empty: no usages → empty result. + Assert.That(viewDefinition.ComputeView(), Is.Empty); + + // Discrimination: PartUsage in usage (not IViewUsage) → excluded. + var partUsage = new PartUsage(); + viewDefinition.AssignOwnership(new FeatureMembership(), partUsage); + + Assert.That(viewDefinition.ComputeView(), Is.Empty); + + // Positive: ViewUsage → included. + var viewUsage = new ViewUsage(); + viewDefinition.AssignOwnership(new FeatureMembership(), viewUsage); + + Assert.That(viewDefinition.ComputeView(), Is.EqualTo([viewUsage])); + + // Multiple: second ViewUsage also returned. + var viewUsage2 = new ViewUsage(); + viewDefinition.AssignOwnership(new FeatureMembership(), viewUsage2); + + Assert.That(viewDefinition.ComputeView(), Is.EqualTo([viewUsage, viewUsage2])); } - + [Test] - public void ComputeViewCondition_ThrowsNotSupportedException() + public void VerifyComputeViewCondition() { - Assert.That(() => ((IViewDefinition)null).ComputeViewCondition(), Throws.TypeOf()); + Assert.That(() => ((IViewDefinition)null).ComputeViewCondition(), Throws.TypeOf()); + + var viewDefinition = new ViewDefinition(); + + // Empty: no ownedMembership → empty result. + Assert.That(viewDefinition.ComputeViewCondition(), Is.Empty); + + // Discrimination: non-ElementFilterMembership in ownedMembership → excluded. + var plainFeature = new Feature(); + var plainMembership = new OwningMembership(); + viewDefinition.AssignOwnership(plainMembership, plainFeature); + + Assert.That(viewDefinition.ComputeViewCondition(), Is.Empty); + + // Positive: ElementFilterMembership with a concrete IExpression → condition included. + var filterCondition = new BooleanExpression(); + var filterMembership = new ElementFilterMembership(); + viewDefinition.AssignOwnership(filterMembership, filterCondition); + + Assert.That(viewDefinition.ComputeViewCondition(), Is.EqualTo([filterCondition])); } - + [Test] - public void ComputeViewRendering_ThrowsNotSupportedException() + public void VerifyComputeViewRendering() { - Assert.That(() => ((IViewDefinition)null).ComputeViewRendering(), Throws.TypeOf()); + Assert.That(() => ((IViewDefinition)null).ComputeViewRendering(), Throws.TypeOf()); + + var viewDefinition = new ViewDefinition(); + + // Empty: no featureMembership → null. + Assert.That(viewDefinition.ComputeViewRendering(), Is.Null); + + // Discrimination: non-ViewRenderingMembership featureMembership → null. + var plainFeature = new Feature(); + var plainFeatureMembership = new FeatureMembership(); + viewDefinition.AssignOwnership(plainFeatureMembership, plainFeature); + + Assert.That(viewDefinition.ComputeViewRendering(), Is.Null); + + // Positive: ViewRenderingMembership whose ownedRendering is a RenderingUsage (no + // ownedReferenceSubsetting) → referencedRendering == ownedRendering → returned. + var renderingUsage = new RenderingUsage(); + var viewRenderingMembership = new ViewRenderingMembership(); + viewDefinition.AssignOwnership(viewRenderingMembership, renderingUsage); + + Assert.That(viewDefinition.ComputeViewRendering(), Is.SameAs(renderingUsage)); + + // Multiple ViewRenderingMemberships: the first one wins (renderings->first() semantics). + var renderingUsage2 = new RenderingUsage(); + var viewRenderingMembership2 = new ViewRenderingMembership(); + viewDefinition.AssignOwnership(viewRenderingMembership2, renderingUsage2); + + Assert.That(viewDefinition.ComputeViewRendering(), Is.SameAs(renderingUsage)); } } } diff --git a/SysML2.NET.Tests/Extend/ViewRenderingMembershipExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/ViewRenderingMembershipExtensionsTestFixture.cs index a3cbef04..0a6a5c4c 100644 --- a/SysML2.NET.Tests/Extend/ViewRenderingMembershipExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/ViewRenderingMembershipExtensionsTestFixture.cs @@ -1,44 +1,131 @@ -// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- // -// +// // 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.Core.POCO.Systems.Parts; using SysML2.NET.Core.POCO.Systems.Views; + using SysML2.NET.Exceptions; + using SysML2.NET.Extensions; [TestFixture] public class ViewRenderingMembershipExtensionsTestFixture { [Test] - public void ComputeOwnedRendering_ThrowsNotSupportedException() + public void VerifyComputeOwnedRendering() { - Assert.That(() => ((IViewRenderingMembership)null).ComputeOwnedRendering(), Throws.TypeOf()); + Assert.That(() => ((IViewRenderingMembership)null).ComputeOwnedRendering(), Throws.TypeOf()); + + // Empty OwnedRelatedElement → [1..1] violation: throws IncompleteModelException. + var emptyMembership = new ViewRenderingMembership(); + + Assert.That(() => emptyMembership.ComputeOwnedRendering(), Throws.TypeOf()); + + // Single non-IRenderingUsage in OwnedRelatedElement (zero matches of target type) → + // [1..1] violation: throws IncompleteModelException. + var nonRenderingMembership = new ViewRenderingMembership(); + var nonRenderingElement = new Namespace(); + ((IContainedRelationship)nonRenderingMembership).OwnedRelatedElement.Add(nonRenderingElement); + + Assert.That(() => nonRenderingMembership.ComputeOwnedRendering(), Throws.TypeOf()); + + // Single IRenderingUsage wired via parent → returned. + var owningDefinition = new ViewDefinition(); + var renderingMembership = new ViewRenderingMembership(); + var renderingUsage = new RenderingUsage(); + owningDefinition.AssignOwnership(renderingMembership, renderingUsage); + + Assert.That(renderingMembership.ComputeOwnedRendering(), Is.SameAs(renderingUsage)); + + // Two IRenderingUsage in OwnedRelatedElement → [1..1] violation: throws IncompleteModelException. + var twoRenderingMembership = new ViewRenderingMembership(); + var firstRendering = new RenderingUsage(); + var secondRendering = new RenderingUsage(); + ((IContainedRelationship)twoRenderingMembership).OwnedRelatedElement.Add(firstRendering); + ((IContainedRelationship)twoRenderingMembership).OwnedRelatedElement.Add(secondRendering); + + Assert.That(() => twoRenderingMembership.ComputeOwnedRendering(), Throws.TypeOf()); + + // Mixed: annotation (Namespace) alongside a single IRenderingUsage — the type filter + // picks out the RenderingUsage regardless of its position. + var mixedMembership = new ViewRenderingMembership(); + var siblingNamespace = new Namespace(); + var mixedRendering = new RenderingUsage(); + ((IContainedRelationship)mixedMembership).OwnedRelatedElement.Add(siblingNamespace); + ((IContainedRelationship)mixedMembership).OwnedRelatedElement.Add(mixedRendering); + + Assert.That(mixedMembership.ComputeOwnedRendering(), Is.SameAs(mixedRendering)); } - + [Test] - public void ComputeReferencedRendering_ThrowsNotSupportedException() + public void VerifyComputeReferencedRendering() { - Assert.That(() => ((IViewRenderingMembership)null).ComputeReferencedRendering(), Throws.TypeOf()); + Assert.That(() => ((IViewRenderingMembership)null).ComputeReferencedRendering(), Throws.TypeOf()); + + // No ownedRendering in OwnedRelatedElement (malformed) → IncompleteModelException + // propagated from ComputeOwnedRendering. + var emptyMembership = new ViewRenderingMembership(); + + Assert.That(() => emptyMembership.ComputeReferencedRendering(), Throws.TypeOf()); + + // Populated, no ownedReferenceSubsetting on the ownedRendering → + // referencedFeature is null → referencedRendering == ownedRendering. + var owningDefinition = new ViewDefinition(); + var renderingMembership = new ViewRenderingMembership(); + var renderingUsage = new RenderingUsage(); + owningDefinition.AssignOwnership(renderingMembership, renderingUsage); + + Assert.That(renderingMembership.ComputeReferencedRendering(), Is.SameAs(renderingUsage)); + + // Populated, with ownedReferenceSubsetting whose ReferencedFeature is itself a + // RenderingUsage → referencedRendering == that RenderingUsage. + var owningDefinition2 = new ViewDefinition(); + var renderingMembership2 = new ViewRenderingMembership(); + var ownedRenderingUsage = new RenderingUsage(); + owningDefinition2.AssignOwnership(renderingMembership2, ownedRenderingUsage); + + var referencedRenderingUsage = new RenderingUsage(); + var referenceSubsetting = new ReferenceSubsetting { ReferencedFeature = referencedRenderingUsage }; + ownedRenderingUsage.AssignOwnership(referenceSubsetting); + + Assert.That(renderingMembership2.ComputeReferencedRendering(), Is.SameAs(referencedRenderingUsage)); + + // Populated, with ownedReferenceSubsetting whose ReferencedFeature is a non-RenderingUsage + // Feature → referencedRendering is null (else null branch of the OCL). + var owningDefinition3 = new ViewDefinition(); + var renderingMembership3 = new ViewRenderingMembership(); + var ownedRenderingUsage3 = new RenderingUsage(); + owningDefinition3.AssignOwnership(renderingMembership3, ownedRenderingUsage3); + + var partUsageTarget = new PartUsage(); + var referenceSubsetting3 = new ReferenceSubsetting { ReferencedFeature = partUsageTarget }; + ownedRenderingUsage3.AssignOwnership(referenceSubsetting3); + + Assert.That(renderingMembership3.ComputeReferencedRendering(), Is.Null); } } } diff --git a/SysML2.NET.Tests/Extend/ViewUsageExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/ViewUsageExtensionsTestFixture.cs index 731f6308..332d594c 100644 --- a/SysML2.NET.Tests/Extend/ViewUsageExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/ViewUsageExtensionsTestFixture.cs @@ -76,7 +76,7 @@ public void VerifyComputeIncludeAsExposedOperation() var element = new Feature(); - // Case (a) — empty conditions: ViewUsage has no ElementFilterMembership in membership; + // 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); @@ -94,7 +94,7 @@ public void VerifyComputeIncludeAsExposedOperation() // false → metadataFeatures.Any(cond.CheckCondition) is false → forAll fails → // element is excluded → returns false. // Note: AnnotationExtensions.ComputeAnnotatingElement remains a NotSupportedException - // stub (C:\CODE\SysML2.NET\SysML2.NET\Extend\AnnotationExtensions.cs), but it is not + // stub (SysML2.NET\SysML2.NET\Extend\AnnotationExtensions.cs), but it is not // reached here because element.ownedAnnotation is empty. var filterCondition = new BooleanExpression(); var filterMembership = new ElementFilterMembership(); @@ -217,15 +217,14 @@ public void VerifyComputeViewRendering() 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. + // Populated: a ViewRenderingMembership whose ownedRendering is a RenderingUsage and + // which has no ownedReferenceSubsetting → ViewRenderingMembership.referencedRendering + // falls back to ownedRendering, so the result is that same RenderingUsage. var renderingUsage = new RenderingUsage(); var viewRenderingMembership = new ViewRenderingMembership(); viewUsage.AssignOwnership(viewRenderingMembership, renderingUsage); - Assert.That(() => viewUsage.ComputeViewRendering(), Throws.TypeOf()); + Assert.That(viewUsage.ComputeViewRendering(), Is.SameAs(renderingUsage)); } } } diff --git a/SysML2.NET.Tests/Extend/ViewpointDefinitionExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/ViewpointDefinitionExtensionsTestFixture.cs index 8134b1b3..1903b971 100644 --- a/SysML2.NET.Tests/Extend/ViewpointDefinitionExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/ViewpointDefinitionExtensionsTestFixture.cs @@ -1,38 +1,54 @@ -// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- // -// +// // 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.Systems.Constraints; + using SysML2.NET.Core.POCO.Systems.Requirements; using SysML2.NET.Core.POCO.Systems.Views; + using SysML2.NET.Extensions; [TestFixture] public class ViewpointDefinitionExtensionsTestFixture { [Test] - public void ComputeViewpointStakeholder_ThrowsNotSupportedException() + public void VerifyComputeViewpointStakeholder() { - Assert.That(() => ((IViewpointDefinition)null).ComputeViewpointStakeholder(), Throws.TypeOf()); + Assert.That(() => ((IViewpointDefinition)null).ComputeViewpointStakeholder(), Throws.TypeOf()); + + var viewpointDefinition = new ViewpointDefinition(); + + // Empty: no framedConcern (no FramedConcernMembership in featureMembership) → empty result. + Assert.That(viewpointDefinition.ComputeViewpointStakeholder(), Is.Empty); + + // Populated case: FramedConcernMembership is present; accessing framedConcern calls + // FramedConcernMembershipExtensions.ComputeOwnedConcern which is an out-of-scope stub. + var framedMembership = new FramedConcernMembership(); + var concernUsage = new ConcernUsage(); + viewpointDefinition.AssignOwnership(framedMembership, concernUsage); + + Assert.That(() => viewpointDefinition.ComputeViewpointStakeholder(), Throws.TypeOf()); } } } diff --git a/SysML2.NET.Tests/Extend/ViewpointUsageExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/ViewpointUsageExtensionsTestFixture.cs index 6beade63..0250100c 100644 --- a/SysML2.NET.Tests/Extend/ViewpointUsageExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/ViewpointUsageExtensionsTestFixture.cs @@ -1,44 +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.Core.Types; + using SysML2.NET.Core.POCO.Systems.Constraints; + using SysML2.NET.Core.POCO.Systems.Requirements; using SysML2.NET.Core.POCO.Systems.Views; + using SysML2.NET.Extensions; [TestFixture] public class ViewpointUsageExtensionsTestFixture { [Test] - public void ComputeViewpointDefinition_ThrowsNotSupportedException() + public void VerifyComputeViewpointDefinition() { - Assert.That(() => ((IViewpointUsage)null).ComputeViewpointDefinition(), Throws.TypeOf()); + Assert.That(() => ((IViewpointUsage)null).ComputeViewpointDefinition(), Throws.TypeOf()); + + var viewpointUsage = new ViewpointUsage(); + + // Empty: no OwnedRelationship → null. + Assert.That(viewpointUsage.ComputeViewpointDefinition(), Is.Null); + + // Negative: FeatureTyping whose Type is a non-ViewpointDefinition (Feature) → null. + var nonViewpointType = new Feature(); + var typingToNonViewpoint = new FeatureTyping { Type = nonViewpointType }; + viewpointUsage.AssignOwnership(typingToNonViewpoint); + + Assert.That(viewpointUsage.ComputeViewpointDefinition(), Is.Null); + + // Positive: FeatureTyping whose Type is a ViewpointDefinition → returned. + var viewpointDefinition = new ViewpointDefinition(); + var typingToViewpointDefinition = new FeatureTyping { Type = viewpointDefinition }; + viewpointUsage.AssignOwnership(typingToViewpointDefinition); + + Assert.That(viewpointUsage.ComputeViewpointDefinition(), Is.SameAs(viewpointDefinition)); + + // Multiple typings: second ViewpointDefinition present; FirstOrDefault returns the first match. + var viewpointDefinition2 = new ViewpointDefinition(); + var typingToViewpointDefinition2 = new FeatureTyping { Type = viewpointDefinition2 }; + viewpointUsage.AssignOwnership(typingToViewpointDefinition2); + + Assert.That(viewpointUsage.ComputeViewpointDefinition(), Is.SameAs(viewpointDefinition)); } - + [Test] - public void ComputeViewpointStakeholder_ThrowsNotSupportedException() + public void VerifyComputeViewpointStakeholder() { - Assert.That(() => ((IViewpointUsage)null).ComputeViewpointStakeholder(), Throws.TypeOf()); + Assert.That(() => ((IViewpointUsage)null).ComputeViewpointStakeholder(), Throws.TypeOf()); + + var viewpointUsage = new ViewpointUsage(); + + // Empty: no framedConcern (no FramedConcernMembership in featureMembership) → empty result. + Assert.That(viewpointUsage.ComputeViewpointStakeholder(), Is.Empty); + + // Populated case: FramedConcernMembership is present; accessing framedConcern calls + // FramedConcernMembershipExtensions.ComputeOwnedConcern which is an out-of-scope stub. + var framedMembership = new FramedConcernMembership(); + var concernUsage = new ConcernUsage(); + viewpointUsage.AssignOwnership(framedMembership, concernUsage); + + Assert.That(() => viewpointUsage.ComputeViewpointStakeholder(), Throws.TypeOf()); } } } diff --git a/SysML2.NET/Extend/RenderingDefinitionExtensions.cs b/SysML2.NET/Extend/RenderingDefinitionExtensions.cs index 25a96c1b..b45ba763 100644 --- a/SysML2.NET/Extend/RenderingDefinitionExtensions.cs +++ b/SysML2.NET/Extend/RenderingDefinitionExtensions.cs @@ -22,6 +22,7 @@ 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; @@ -74,10 +75,11 @@ internal static class RenderingDefinitionExtensions /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeRendering(this IRenderingDefinition renderingDefinitionSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return renderingDefinitionSubject == null + ? throw new ArgumentNullException(nameof(renderingDefinitionSubject)) + : [..renderingDefinitionSubject.usage.OfType()]; } } diff --git a/SysML2.NET/Extend/RenderingUsageExtensions.cs b/SysML2.NET/Extend/RenderingUsageExtensions.cs index e2423955..5da2be50 100644 --- a/SysML2.NET/Extend/RenderingUsageExtensions.cs +++ b/SysML2.NET/Extend/RenderingUsageExtensions.cs @@ -22,6 +22,7 @@ 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; @@ -71,10 +72,15 @@ internal static class RenderingUsageExtensions /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IRenderingDefinition ComputeRenderingDefinition(this IRenderingUsage renderingUsageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return renderingUsageSubject == null + ? throw new ArgumentNullException(nameof(renderingUsageSubject)) + : renderingUsageSubject.OwnedRelationship + .OfType() + .Select(featureTyping => featureTyping.Type) + .OfType() + .FirstOrDefault(); } } diff --git a/SysML2.NET/Extend/ViewDefinitionExtensions.cs b/SysML2.NET/Extend/ViewDefinitionExtensions.cs index 348f6912..733d9144 100644 --- a/SysML2.NET/Extend/ViewDefinitionExtensions.cs +++ b/SysML2.NET/Extend/ViewDefinitionExtensions.cs @@ -22,6 +22,7 @@ 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; @@ -29,6 +30,7 @@ namespace SysML2.NET.Core.POCO.Systems.Views 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.Annotations; using SysML2.NET.Core.POCO.Root.Elements; using SysML2.NET.Core.POCO.Root.Namespaces; @@ -77,10 +79,11 @@ internal static class ViewDefinitionExtensions /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeSatisfiedViewpoint(this IViewDefinition viewDefinitionSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return viewDefinitionSubject == null + ? throw new ArgumentNullException(nameof(viewDefinitionSubject)) + : [..viewDefinitionSubject.ownedRequirement.OfType().Where(viewpointUsage => viewpointUsage.IsComposite)]; } /// @@ -98,10 +101,11 @@ internal static List ComputeSatisfiedViewpoint(this IViewDefini /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeView(this IViewDefinition viewDefinitionSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return viewDefinitionSubject == null + ? throw new ArgumentNullException(nameof(viewDefinitionSubject)) + : [..viewDefinitionSubject.usage.OfType()]; } /// @@ -121,10 +125,16 @@ internal static List ComputeView(this IViewDefinition viewDefinition /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeViewCondition(this IViewDefinition viewDefinitionSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return viewDefinitionSubject == null + ? throw new ArgumentNullException(nameof(viewDefinitionSubject)) + : [ + ..viewDefinitionSubject.ownedMembership + .OfType() + .Select(elementFilterMembership => elementFilterMembership.condition) + .Where(condition => condition != null) + ]; } /// @@ -147,10 +157,16 @@ internal static List ComputeViewCondition(this IViewDefinition view /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IRenderingUsage ComputeViewRendering(this IViewDefinition viewDefinitionSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (viewDefinitionSubject == null) + { + throw new ArgumentNullException(nameof(viewDefinitionSubject)); + } + + var renderings = viewDefinitionSubject.featureMembership.OfType().ToList(); + + return renderings.Count == 0 ? null : renderings[0].referencedRendering; } } diff --git a/SysML2.NET/Extend/ViewRenderingMembershipExtensions.cs b/SysML2.NET/Extend/ViewRenderingMembershipExtensions.cs index 2e799cd4..277f53d5 100644 --- a/SysML2.NET/Extend/ViewRenderingMembershipExtensions.cs +++ b/SysML2.NET/Extend/ViewRenderingMembershipExtensions.cs @@ -22,8 +22,10 @@ namespace SysML2.NET.Core.POCO.Systems.Views { using System; using System.Collections.Generic; + using System.Linq; using SysML2.NET.Core.Root.Namespaces; + using SysML2.NET.Extensions; using SysML2.NET.Core.POCO.Core.Features; using SysML2.NET.Core.POCO.Core.Types; using SysML2.NET.Core.POCO.Root.Annotations; @@ -45,25 +47,55 @@ internal static class ViewRenderingMembershipExtensions /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IRenderingUsage ComputeOwnedRendering(this IViewRenderingMembership viewRenderingMembershipSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (viewRenderingMembershipSubject == null) + { + throw new ArgumentNullException(nameof(viewRenderingMembershipSubject)); + } + + return viewRenderingMembershipSubject.OwnedRelatedElement.RequireSingleOfType(nameof(viewRenderingMembershipSubject)); } /// /// Computes the derived property. /// + /// + /// OCL2.0: + /// + /// referencedRendering = + /// let referencedFeature : Feature = + /// ownedRendering.referencedFeatureTarget() in + /// if referencedFeature = null then ownedRendering + /// else if referencedFeature.oclIsKindOf(RenderingUsage) then + /// refrencedFeature.oclAsType(RenderingUsage) + /// else null + /// endif endif + /// + /// /// /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IRenderingUsage ComputeReferencedRendering(this IViewRenderingMembership viewRenderingMembershipSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (viewRenderingMembershipSubject == null) + { + throw new ArgumentNullException(nameof(viewRenderingMembershipSubject)); + } + + var ownedRendering = viewRenderingMembershipSubject.ownedRendering; + + var referencedFeature = ownedRendering?.ReferencedFeatureTarget(); + + if (referencedFeature == null) + { + return ownedRendering; + } + + return referencedFeature as IRenderingUsage; } } diff --git a/SysML2.NET/Extend/ViewpointDefinitionExtensions.cs b/SysML2.NET/Extend/ViewpointDefinitionExtensions.cs index 5058444b..127a26b1 100644 --- a/SysML2.NET/Extend/ViewpointDefinitionExtensions.cs +++ b/SysML2.NET/Extend/ViewpointDefinitionExtensions.cs @@ -22,6 +22,7 @@ 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; @@ -78,10 +79,17 @@ internal static class ViewpointDefinitionExtensions /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeViewpointStakeholder(this IViewpointDefinition viewpointDefinitionSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + // The OCL uses "featureMemberhsip" which is a typo in the XMI source; the correct C# property is featureMembership. + return viewpointDefinitionSubject == null + ? throw new ArgumentNullException(nameof(viewpointDefinitionSubject)) + : [ + ..viewpointDefinitionSubject.framedConcern + .SelectMany(concern => concern.featureMembership) + .OfType() + .Select(stakeholderMembership => stakeholderMembership.ownedStakeholderParameter) + ]; } } diff --git a/SysML2.NET/Extend/ViewpointUsageExtensions.cs b/SysML2.NET/Extend/ViewpointUsageExtensions.cs index 0165ae27..2662b0da 100644 --- a/SysML2.NET/Extend/ViewpointUsageExtensions.cs +++ b/SysML2.NET/Extend/ViewpointUsageExtensions.cs @@ -22,6 +22,7 @@ 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; @@ -72,10 +73,15 @@ internal static class ViewpointUsageExtensions /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IViewpointDefinition ComputeViewpointDefinition(this IViewpointUsage viewpointUsageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return viewpointUsageSubject == null + ? throw new ArgumentNullException(nameof(viewpointUsageSubject)) + : viewpointUsageSubject.OwnedRelationship + .OfType() + .Select(featureTyping => featureTyping.Type) + .OfType() + .FirstOrDefault(); } /// @@ -95,10 +101,17 @@ internal static IViewpointDefinition ComputeViewpointDefinition(this IViewpointU /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeViewpointStakeholder(this IViewpointUsage viewpointUsageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + // The OCL uses "featureMemberhsip" which is a typo in the XMI source; the correct C# property is featureMembership. + return viewpointUsageSubject == null + ? throw new ArgumentNullException(nameof(viewpointUsageSubject)) + : [ + ..viewpointUsageSubject.framedConcern + .SelectMany(concern => concern.featureMembership) + .OfType() + .Select(stakeholderMembership => stakeholderMembership.ownedStakeholderParameter) + ]; } }