diff --git a/.claude/commands/implement-extensions-batch.md b/.claude/commands/implement-extensions-batch.md index ceacc30c..a6a2bd7a 100644 --- a/.claude/commands/implement-extensions-batch.md +++ b/.claude/commands/implement-extensions-batch.md @@ -1,5 +1,5 @@ --- -description: Spawn the 4-role team across N SysML2.NET Extend files in one run — creates a batch branch, assigns the related GitHub issues to the user, and updates each issue's checklist on completion +description: Spawn the 4-role team across N SysML2.NET Extend files in one run — creates and pushes a batch branch, assigns the related GitHub issues to the user, and updates each issue's checklist on completion argument-hint: [ ...] (2–6 Extension file names; each will be normalised to SysML2.NET/Extend/Extensions.cs) --- @@ -13,7 +13,9 @@ single-file flow: 1. **Pre-flight validation** of every file + its GitHub issue, before any state change. 2. **Creates a new git branch** off `development` with a deterministic name - derived from the batch's issue numbers. + derived from the batch's issue numbers, AND pushes it to `origin` with + upstream tracking set so the user can immediately open a pull request after + committing. 3. **Assigns every related GitHub issue to the invoking user** (`@me`). 4. **Parallelises agent spawns across files** wherever their target files are disjoint. @@ -138,11 +140,25 @@ batch-impl-extensions- - If more than 4 issues: include the first 4 + `-plus` suffix (e.g. `batch-impl-extensions-123-180-186-190-plus2` for N=6). -Create: +Create locally **and immediately publish to `origin` with upstream tracking**: ```bash git switch -c origin/development +git push -u origin ``` +The push lifts the branch onto the remote at the same commit as +`origin/development` (no diff yet — that comes after the batch's edits + the +user's commit). Setting upstream now means: +- The user's eventual `git push` after committing needs no flags. +- A pull request can be opened via the GitHub UI or `gh pr create` as soon as + the user pushes their first commit, without an additional `git push -u` + step. + +If the `git push -u` fails (network, auth, branch-protection refusing empty +pushes), log the failure but **continue with the batch**. The implementation +work is the main goal; the branch will still exist locally and the user can +re-push manually at the end. Surface the failure clearly in the final summary. + Refuse if the branch already exists locally OR on origin (`git ls-remote --exit-code origin `) — ask the user to pick a different batch or delete the stale branch. @@ -253,7 +269,10 @@ step-11 logic from `/implement-extensions`: Print to the user: -- **Branch**: name + base ref + how to delete-if-aborting. +- **Branch**: name + base ref + remote-tracking state (`pushed to origin` / + `local only — push failed at step 6, push manually with: git push -u origin `) + + how to delete-if-aborting (locally: `git branch -D `; remotely if + pushed: `git push origin --delete `). - **Per-file table**: | File | Stubs impl. | Targeted tests | Reg. sweep impact | Reviewer | Issue | @@ -269,8 +288,11 @@ Print to the user: - Out-of-scope blockers surfaced (e.g. "VerifyComputeX in TestFixture is still stub-blocked on `` — consider a follow-up issue"). -- **Reminder**: nothing is auto-committed. User reviews `git diff`, decides - whether to commit / push / open PR. +- **Reminder**: nothing is auto-committed. The branch exists locally (and on + `origin` with upstream tracking, when step 6's push succeeded). User reviews + `git diff`, commits, then `git push` (no flags needed — tracking is already + set) and opens the PR via `gh pr create --base development --head ` + or the GitHub UI. ## Failure handling @@ -281,6 +303,7 @@ Print to the user: | Ambiguous issue | Step 2 | `AskUserQuestion` for an explicit issue number per file. | | Dirty working tree | Step 4 | Abort, ask user to commit/stash. | | Branch already exists | Step 6 | Abort; ask user to pick a different batch or delete the stale branch. | +| `git push -u origin ` fails (network, auth, branch protection) | Step 6 | Log + continue (non-blocking; surface clearly in step 14 final summary with a manual re-push command). | | `gh issue edit --add-assignee` fails for one issue | Step 7 | Log + continue (non-blocking; implementation still proceeds). | | Production build fails after implementer | Step 10.1 | Attribute to the file, re-dispatch that implementer. | | Targeted test fails | Step 10.2 | Attribute (OCL vs test bug), re-dispatch correct role. | diff --git a/SysML2.NET.Tests/Extend/ExpressionExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/ExpressionExtensionsTestFixture.cs index b8fb5643..47fccad3 100644 --- a/SysML2.NET.Tests/Extend/ExpressionExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/ExpressionExtensionsTestFixture.cs @@ -1,20 +1,20 @@ // ------------------------------------------------------------------------------------------------- // -// +// // 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. -// +// // // ------------------------------------------------------------------------------------------------ @@ -28,15 +28,73 @@ namespace SysML2.NET.Tests.Extend using SysML2.NET.Core.Core.Types; using SysML2.NET.Core.POCO.Core.Features; using SysML2.NET.Core.POCO.Core.Types; - using SysML2.NET.Core.POCO.Kernel.Expressions; using SysML2.NET.Core.POCO.Kernel.FeatureValues; using SysML2.NET.Core.POCO.Kernel.Functions; - using SysML2.NET.Core.POCO.Root.Elements; using SysML2.NET.Extensions; + using Type = SysML2.NET.Core.POCO.Core.Types.Type; + [TestFixture] public class ExpressionExtensionsTestFixture { + [Test] + public void VerifyComputeCheckConditionOperation() + { + // Null guard on subject. + Assert.That(() => ((IExpression)null).ComputeCheckConditionOperation(null), Throws.TypeOf()); + + var expression = new Expression(); + + // target == null: forwarded to Evaluate; empty-resultExprs branch returns [] → false (Count != 1). + Assert.That(expression.ComputeCheckConditionOperation(null), Is.False); + + // Empty: Evaluate returns [] (no ResultExpressionMembership) → false (Count != 1). + Assert.That(expression.ComputeCheckConditionOperation(null), Is.False); + + // Populated case: ResultExpressionMembership wired — Evaluate delegates through the inner + // (empty) Expression and returns the empty list. CheckCondition requires Count == 1 AND + // a LiteralBoolean { Value: true }, so it returns false. + var expressionWithRem = new Expression(); + var resultExprMembership = new ResultExpressionMembership(); + var innerExpression = new Expression(); + expressionWithRem.AssignOwnership(resultExprMembership, innerExpression); + + Assert.That(expressionWithRem.ComputeCheckConditionOperation(null), Is.False); + } + + [Test] + public void VerifyComputeEvaluateOperation() + { + // Null guard on subject. + Assert.That(() => ((IExpression)null).ComputeEvaluateOperation(null), Throws.TypeOf()); + + var expression = new Expression(); + + // target == null is permitted for the empty branch (base body doesn't dereference target). + Assert.That(() => expression.ComputeEvaluateOperation(null), Throws.Nothing); + + // Empty: no IResultExpressionMembership in ownedFeatureMembership → returns empty list. + Assert.That(expression.ComputeEvaluateOperation(null), Is.Empty); + + // Discrimination: FeatureMembership with non-ResultExpressionMembership type → still empty. + var plainMembership = new FeatureMembership(); + var plainFeature = new Feature(); + expression.AssignOwnership(plainMembership, plainFeature); + + Assert.That(expression.ComputeEvaluateOperation(null), Is.Empty); + + // Populated case: ResultExpressionMembership wired — the production code resolves + // resultExpressionMembership.ownedResultExpression (the inner Expression) and recursively + // calls Evaluate on it. The inner Expression is itself empty (no nested + // ResultExpressionMembership), so its Evaluate returns the empty list. + var expressionWithRem = new Expression(); + var resultExprMembership = new ResultExpressionMembership(); + var innerExpression = new Expression(); + expressionWithRem.AssignOwnership(resultExprMembership, innerExpression); + + Assert.That(expressionWithRem.ComputeEvaluateOperation(null), Is.Empty); + } + [Test] public void VerifyComputeFunction() { @@ -48,7 +106,7 @@ public void VerifyComputeFunction() Assert.That(expression.ComputeFunction(), Is.Null); // Negative: FeatureTyping pointing at a non-Function Type → null. - var nonFunctionType = new SysML2.NET.Core.POCO.Core.Types.Type(); + var nonFunctionType = new Type(); var typingToNonFunction = new FeatureTyping { Type = nonFunctionType }; expression.AssignOwnership(typingToNonFunction); @@ -93,38 +151,6 @@ public void VerifyComputeIsModelLevelEvaluable() Assert.That(expressionWithNonImplied.ComputeIsModelLevelEvaluable(), Is.False); } - [Test] - public void VerifyComputeResult() - { - Assert.That(() => ((IExpression)null).ComputeResult(), Throws.TypeOf()); - - var expression = new Expression(); - - // Empty: no featureMembership → null. - Assert.That(expression.ComputeResult(), Is.Null); - - // Negative: FeatureMembership with non-ReturnParameterMembership → null. - var featureMembership = new FeatureMembership(); - var plainFeature = new Feature(); - expression.AssignOwnership(featureMembership, plainFeature); - - Assert.That(expression.ComputeResult(), Is.Null); - - // Positive: one ReturnParameterMembership with ownedMemberParameter set → that Feature returned. - var resultFeature = new Feature(); - var returnParamMembership = new ReturnParameterMembership(); - expression.AssignOwnership(returnParamMembership, resultFeature); - - Assert.That(expression.ComputeResult(), Is.SameAs(resultFeature)); - - // Multiple ReturnParameterMemberships (illegal per OCL validation but tolerated) → first returned. - var secondResultFeature = new Feature(); - var secondReturnParamMembership = new ReturnParameterMembership(); - expression.AssignOwnership(secondReturnParamMembership, secondResultFeature); - - Assert.That(expression.ComputeResult(), Is.SameAs(resultFeature)); - } - [Test] public void VerifyComputeModelLevelEvaluableOperation() { @@ -205,85 +231,49 @@ public void VerifyComputeModelLevelEvaluableOperation() Assert.That(expressionWithValuation.ComputeModelLevelEvaluableOperation([]), Is.True); - // BRANCH_B: ownedFeature under ResultExpressionMembership — STUB-BLOCKER. - // Although BRANCH_B logic only accesses owningFeatureMembership and calls ModelLevelEvaluable - // on the inner Expression, the production code first computes expressionSubject.result (line 157 - // of ExpressionExtensions.cs), which traverses featureMembership → inheritedMembership → - // ownedFeature → ResultExpressionMembership.ownedMemberFeature → ownedResultExpression → - // ResultExpressionMembershipExtensions.ComputeOwnedResultExpression, which is a stub. - // Therefore the BRANCH_B recursion path cannot be exercised until that stub is implemented. + // BRANCH_B: ownedFeature owned via ResultExpressionMembership — passes branchB. + // The inner Expression is empty (no specializations, no features), so its recursive + // ModelLevelEvaluable call returns true; the outer Expression's single owned feature + // satisfies branchB (owningFeatureMembership is IResultExpressionMembership AND + // innerExpression.ModelLevelEvaluable is true). Outer returns true. var expressionBranchB = new Expression(); var innerExpression = new Expression(); var resultExprMembershipB = new ResultExpressionMembership(); expressionBranchB.AssignOwnership(resultExprMembershipB, innerExpression); - Assert.That( - () => expressionBranchB.ComputeModelLevelEvaluableOperation([]), - Throws.TypeOf()); + Assert.That(expressionBranchB.ComputeModelLevelEvaluableOperation([]), Is.True); } [Test] - public void VerifyComputeEvaluateOperation() + public void VerifyComputeResult() { - // Null guard on subject. - Assert.That(() => ((IExpression)null).ComputeEvaluateOperation(null), Throws.TypeOf()); + Assert.That(() => ((IExpression)null).ComputeResult(), Throws.TypeOf()); var expression = new Expression(); - // target == null is permitted for the empty branch (base body doesn't dereference target). - Assert.That(() => expression.ComputeEvaluateOperation(null), Throws.Nothing); - - // Empty: no IResultExpressionMembership in ownedFeatureMembership → returns empty list. - Assert.That(expression.ComputeEvaluateOperation(null), Is.Empty); + // Empty: no featureMembership → null. + Assert.That(expression.ComputeResult(), Is.Null); - // Discrimination: FeatureMembership with non-ResultExpressionMembership type → still empty. - var plainMembership = new FeatureMembership(); + // Negative: FeatureMembership with non-ReturnParameterMembership → null. + var featureMembership = new FeatureMembership(); var plainFeature = new Feature(); - expression.AssignOwnership(plainMembership, plainFeature); - - Assert.That(expression.ComputeEvaluateOperation(null), Is.Empty); - - // STUB-BLOCKER: wire a ResultExpressionMembership → the production code calls - // resultExpressionMembership.ownedResultExpression, which dispatches to - // ResultExpressionMembershipExtensions.ComputeOwnedResultExpression, which is a stub - // that throws NotSupportedException. Assert the stub propagates. - var expressionWithRem = new Expression(); - var resultExprMembership = new ResultExpressionMembership(); - var innerExpression = new Expression(); - expressionWithRem.AssignOwnership(resultExprMembership, innerExpression); - - // ResultExpressionMembershipExtensions.ComputeOwnedResultExpression is a stub — NSE expected. - Assert.That( - () => expressionWithRem.ComputeEvaluateOperation(null), - Throws.TypeOf()); - } - - [Test] - public void VerifyComputeCheckConditionOperation() - { - // Null guard on subject. - Assert.That(() => ((IExpression)null).ComputeCheckConditionOperation(null), Throws.TypeOf()); + expression.AssignOwnership(featureMembership, plainFeature); - var expression = new Expression(); + Assert.That(expression.ComputeResult(), Is.Null); - // target == null: forwarded to Evaluate; empty-resultExprs branch returns [] → false (Count != 1). - Assert.That(expression.ComputeCheckConditionOperation(null), Is.False); + // Positive: one ReturnParameterMembership with ownedMemberParameter set → that Feature returned. + var resultFeature = new Feature(); + var returnParamMembership = new ReturnParameterMembership(); + expression.AssignOwnership(returnParamMembership, resultFeature); - // Empty: Evaluate returns [] (no ResultExpressionMembership) → false (Count != 1). - Assert.That(expression.ComputeCheckConditionOperation(null), Is.False); + Assert.That(expression.ComputeResult(), Is.SameAs(resultFeature)); - // STUB-BLOCKER: wire a ResultExpressionMembership → Evaluate delegates to ComputeEvaluateOperation, - // which accesses resultExpressionMembership.ownedResultExpression → - // ResultExpressionMembershipExtensions.ComputeOwnedResultExpression is a stub → NSE propagates. - var expressionWithRem = new Expression(); - var resultExprMembership = new ResultExpressionMembership(); - var innerExpression = new Expression(); - expressionWithRem.AssignOwnership(resultExprMembership, innerExpression); + // Multiple ReturnParameterMemberships (illegal per OCL validation but tolerated) → first returned. + var secondResultFeature = new Feature(); + var secondReturnParamMembership = new ReturnParameterMembership(); + expression.AssignOwnership(secondReturnParamMembership, secondResultFeature); - // ResultExpressionMembershipExtensions.ComputeOwnedResultExpression is a stub — NSE expected. - Assert.That( - () => expressionWithRem.ComputeCheckConditionOperation(null), - Throws.TypeOf()); + Assert.That(expression.ComputeResult(), Is.SameAs(resultFeature)); } } } diff --git a/SysML2.NET.Tests/Extend/ObjectiveMembershipExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/ObjectiveMembershipExtensionsTestFixture.cs index cc308bcf..b64d3632 100644 --- a/SysML2.NET.Tests/Extend/ObjectiveMembershipExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/ObjectiveMembershipExtensionsTestFixture.cs @@ -21,18 +21,69 @@ namespace SysML2.NET.Tests.Extend { using System; - + using NUnit.Framework; - + + using SysML2.NET.Core.POCO.Root.Elements; + using SysML2.NET.Core.POCO.Root.Namespaces; using SysML2.NET.Core.POCO.Systems.Cases; + using SysML2.NET.Core.POCO.Systems.Requirements; + using SysML2.NET.Exceptions; + using SysML2.NET.Extensions; + + using Type = SysML2.NET.Core.POCO.Core.Types.Type; [TestFixture] public class ObjectiveMembershipExtensionsTestFixture { [Test] - public void ComputeOwnedObjectiveRequirement_ThrowsNotSupportedException() + public void VerifyComputeOwnedObjectiveRequirement() { - Assert.That(() => ((IObjectiveMembership)null).ComputeOwnedObjectiveRequirement(), Throws.TypeOf()); + Assert.That(() => ((IObjectiveMembership)null).ComputeOwnedObjectiveRequirement(), Throws.TypeOf()); + + // Empty OwnedRelatedElement → [1..1] violation: throws IncompleteModelException. + var objectiveMembership = new ObjectiveMembership(); + + Assert.That(() => objectiveMembership.ComputeOwnedObjectiveRequirement(), Throws.TypeOf()); + + // Single IRequirementUsage wired via the public API → returned. + var owningType = new Type(); + var objectiveRequirement = new RequirementUsage(); + + owningType.AssignOwnership(objectiveMembership, objectiveRequirement); + + Assert.That(objectiveMembership.ComputeOwnedObjectiveRequirement(), Is.SameAs(objectiveRequirement)); + + // Two IRequirementUsages in OwnedRelatedElement → [1..1] violation: throws IncompleteModelException. + var twoRequirementMembership = new ObjectiveMembership(); + var firstRequirement = new RequirementUsage(); + var secondRequirement = new RequirementUsage(); + + ((IContainedRelationship)twoRequirementMembership).OwnedRelatedElement.Add(firstRequirement); + ((IContainedRelationship)twoRequirementMembership).OwnedRelatedElement.Add(secondRequirement); + + Assert.That(() => twoRequirementMembership.ComputeOwnedObjectiveRequirement(), Throws.TypeOf()); + + // Mixed-type owned related elements: exactly one IRequirementUsage alongside a non-IRequirementUsage (Namespace). + // The OfType() projection MUST pick out the IRequirementUsage regardless of its position + // (this is the core robustness guarantee — never positionally index the unfiltered collection). + var mixedMembership = new ObjectiveMembership(); + var siblingNonRequirement = new Namespace(); + var mixedRequirement = new RequirementUsage(); + + ((IContainedRelationship)mixedMembership).OwnedRelatedElement.Add(siblingNonRequirement); + ((IContainedRelationship)mixedMembership).OwnedRelatedElement.Add(mixedRequirement); + + Assert.That(mixedMembership.ComputeOwnedObjectiveRequirement(), Is.SameAs(mixedRequirement)); + + // OwnedRelatedElement populated with non-IRequirementUsage element(s) only → no IRequirementUsage match: + // [1..1] violation, throws IncompleteModelException. + var nonRequirementMembership = new ObjectiveMembership(); + var nonRequirementElement = new Namespace(); + + ((IContainedRelationship)nonRequirementMembership).OwnedRelatedElement.Add(nonRequirementElement); + + Assert.That(() => nonRequirementMembership.ComputeOwnedObjectiveRequirement(), Throws.TypeOf()); } } } diff --git a/SysML2.NET.Tests/Extend/RequirementDefinitionExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/RequirementDefinitionExtensionsTestFixture.cs index b3b2acde..216eeefd 100644 --- a/SysML2.NET.Tests/Extend/RequirementDefinitionExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/RequirementDefinitionExtensionsTestFixture.cs @@ -170,14 +170,13 @@ public void VerifyComputeStakeholderParameter() // identical to the populated stub-blocker case below. // The discrimination block is omitted until upstream stubs are implemented. - // Populated case: StakeholderMembership is present; selecting ownedStakeholderParameter - // triggers an upstream stub (StakeholderMembershipExtensions.ComputeOwnedStakeholderParameter - // is not yet implemented). + // Populated case: StakeholderMembership is present — ownedStakeholderParameter surfaces + // the wired PartUsage and ComputeStakeholderParameter returns it. var stakeholderMembership = new StakeholderMembership(); var stakeholderPartUsage = new PartUsage(); requirementDefinition.AssignOwnership(stakeholderMembership, stakeholderPartUsage); - Assert.That(() => requirementDefinition.ComputeStakeholderParameter(), Throws.TypeOf()); + Assert.That(requirementDefinition.ComputeStakeholderParameter(), Is.EqualTo([stakeholderPartUsage])); } [Test] diff --git a/SysML2.NET.Tests/Extend/RequirementUsageExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/RequirementUsageExtensionsTestFixture.cs index 6a75eab3..6ecc53d4 100644 --- a/SysML2.NET.Tests/Extend/RequirementUsageExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/RequirementUsageExtensionsTestFixture.cs @@ -203,14 +203,13 @@ public void VerifyComputeStakeholderParameter() // identical to the populated stub-blocker case below. // The discrimination block is omitted until upstream stubs are implemented. - // Populated case: StakeholderMembership is present; selecting ownedStakeholderParameter - // triggers an upstream stub (StakeholderMembershipExtensions.ComputeOwnedStakeholderParameter - // is not yet implemented). + // Populated case: StakeholderMembership is present — ownedStakeholderParameter surfaces + // the wired PartUsage and ComputeStakeholderParameter returns it. var stakeholderMembership = new StakeholderMembership(); var stakeholderPartUsage = new PartUsage(); requirementUsage.AssignOwnership(stakeholderMembership, stakeholderPartUsage); - Assert.That(() => requirementUsage.ComputeStakeholderParameter(), Throws.TypeOf()); + Assert.That(requirementUsage.ComputeStakeholderParameter(), Is.EqualTo([stakeholderPartUsage])); } [Test] diff --git a/SysML2.NET.Tests/Extend/ResultExpressionMembershipExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/ResultExpressionMembershipExtensionsTestFixture.cs index 035182f2..56ef6824 100644 --- a/SysML2.NET.Tests/Extend/ResultExpressionMembershipExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/ResultExpressionMembershipExtensionsTestFixture.cs @@ -21,18 +21,69 @@ namespace SysML2.NET.Tests.Extend { using System; - + using NUnit.Framework; - + + using SysML2.NET.Core.POCO.Kernel.Expressions; using SysML2.NET.Core.POCO.Kernel.Functions; + using SysML2.NET.Core.POCO.Root.Elements; + using SysML2.NET.Exceptions; + using SysML2.NET.Extensions; + + using Type = SysML2.NET.Core.POCO.Core.Types.Type; [TestFixture] public class ResultExpressionMembershipExtensionsTestFixture { [Test] - public void ComputeOwnedResultExpression_ThrowsNotSupportedException() + public void VerifyComputeOwnedResultExpression() { - Assert.That(() => ((IResultExpressionMembership)null).ComputeOwnedResultExpression(), Throws.TypeOf()); + Assert.That(() => ((IResultExpressionMembership)null).ComputeOwnedResultExpression(), Throws.TypeOf()); + + // Empty OwnedRelatedElement → [1..1] violation: throws IncompleteModelException. + var emptyMembership = new ResultExpressionMembership(); + + Assert.That(() => emptyMembership.ComputeOwnedResultExpression(), Throws.TypeOf()); + + // Single IExpression wired via the public API → returned. + var owningType = new Type(); + var resultExpressionMembership = new ResultExpressionMembership(); + var literalBoolean = new LiteralBoolean(); + + owningType.AssignOwnership(resultExpressionMembership, literalBoolean); + + Assert.That(resultExpressionMembership.ComputeOwnedResultExpression(), Is.SameAs(literalBoolean)); + + // Two IExpressions in OwnedRelatedElement → [1..1] violation: throws IncompleteModelException. + var twoExprMembership = new ResultExpressionMembership(); + var firstExpression = new LiteralBoolean(); + var secondExpression = new LiteralBoolean(); + + ((IContainedRelationship)twoExprMembership).OwnedRelatedElement.Add(firstExpression); + ((IContainedRelationship)twoExprMembership).OwnedRelatedElement.Add(secondExpression); + + Assert.That(() => twoExprMembership.ComputeOwnedResultExpression(), Throws.TypeOf()); + + // Mixed-type owned related elements: exactly one IExpression alongside a non-IExpression (Type). + // The OfType() projection MUST pick out the IExpression regardless of its position + // (this is the core robustness guarantee — never positionally index the unfiltered collection). + var mixedMembership = new ResultExpressionMembership(); + var siblingNonExpression = new Type(); + var mixedExpression = new LiteralBoolean(); + + ((IContainedRelationship)mixedMembership).OwnedRelatedElement.Add(siblingNonExpression); + ((IContainedRelationship)mixedMembership).OwnedRelatedElement.Add(mixedExpression); + + Assert.That(mixedMembership.ComputeOwnedResultExpression(), Is.SameAs(mixedExpression)); + + // OwnedRelatedElement populated with non-IExpression element(s) only → no IExpression match: + // [1..1] violation, throws IncompleteModelException. + var nonExprMembership = new ResultExpressionMembership(); + var nonExprElement = new Type(); + + ((IContainedRelationship)nonExprMembership).OwnedRelatedElement.Add(nonExprElement); + + Assert.That(() => nonExprMembership.ComputeOwnedResultExpression(), Throws.TypeOf()); } } } diff --git a/SysML2.NET.Tests/Extend/StakeholderMembershipExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/StakeholderMembershipExtensionsTestFixture.cs index 50dd4366..e9788431 100644 --- a/SysML2.NET.Tests/Extend/StakeholderMembershipExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/StakeholderMembershipExtensionsTestFixture.cs @@ -21,18 +21,69 @@ namespace SysML2.NET.Tests.Extend { using System; - + using NUnit.Framework; - + + 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.Requirements; + using SysML2.NET.Exceptions; + using SysML2.NET.Extensions; + + using Type = SysML2.NET.Core.POCO.Core.Types.Type; [TestFixture] public class StakeholderMembershipExtensionsTestFixture { [Test] - public void ComputeOwnedStakeholderParameter_ThrowsNotSupportedException() + public void VerifyComputeOwnedStakeholderParameter() { - Assert.That(() => ((IStakeholderMembership)null).ComputeOwnedStakeholderParameter(), Throws.TypeOf()); + Assert.That(() => ((IStakeholderMembership)null).ComputeOwnedStakeholderParameter(), Throws.TypeOf()); + + // Empty OwnedRelatedElement → [1..1] violation: throws IncompleteModelException. + var stakeholderMembership = new StakeholderMembership(); + + Assert.That(() => stakeholderMembership.ComputeOwnedStakeholderParameter(), Throws.TypeOf()); + + // Single IPartUsage wired via the public API → returned. + var owningType = new Type(); + var stakeholderPartUsage = new PartUsage(); + + owningType.AssignOwnership(stakeholderMembership, stakeholderPartUsage); + + Assert.That(stakeholderMembership.ComputeOwnedStakeholderParameter(), Is.SameAs(stakeholderPartUsage)); + + // Two IPartUsages in OwnedRelatedElement → [1..1] violation: throws IncompleteModelException. + var twoPartMembership = new StakeholderMembership(); + var firstPart = new PartUsage(); + var secondPart = new PartUsage(); + + ((IContainedRelationship)twoPartMembership).OwnedRelatedElement.Add(firstPart); + ((IContainedRelationship)twoPartMembership).OwnedRelatedElement.Add(secondPart); + + Assert.That(() => twoPartMembership.ComputeOwnedStakeholderParameter(), Throws.TypeOf()); + + // Mixed-type owned related elements: exactly one IPartUsage alongside a non-IPartUsage (Namespace). + // The RequireSingleOfType projection MUST pick out the IPartUsage regardless of position + // (this is the core robustness guarantee — never positionally index the unfiltered collection). + var mixedMembership = new StakeholderMembership(); + var siblingNonPart = new Namespace(); + var mixedPart = new PartUsage(); + + ((IContainedRelationship)mixedMembership).OwnedRelatedElement.Add(siblingNonPart); + ((IContainedRelationship)mixedMembership).OwnedRelatedElement.Add(mixedPart); + + Assert.That(mixedMembership.ComputeOwnedStakeholderParameter(), Is.SameAs(mixedPart)); + + // OwnedRelatedElement populated with non-IPartUsage element(s) only → no IPartUsage match: + // [1..1] violation, throws IncompleteModelException. + var nonPartMembership = new StakeholderMembership(); + var nonPartElement = new Namespace(); + + ((IContainedRelationship)nonPartMembership).OwnedRelatedElement.Add(nonPartElement); + + Assert.That(() => nonPartMembership.ComputeOwnedStakeholderParameter(), Throws.TypeOf()); } } } diff --git a/SysML2.NET.Tests/Extend/StateSubactionMembershipExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/StateSubactionMembershipExtensionsTestFixture.cs index 9eb5bdb3..2479b80f 100644 --- a/SysML2.NET.Tests/Extend/StateSubactionMembershipExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/StateSubactionMembershipExtensionsTestFixture.cs @@ -21,18 +21,69 @@ namespace SysML2.NET.Tests.Extend { using System; - + using NUnit.Framework; - + + 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.States; + using SysML2.NET.Core.Systems.States; + using SysML2.NET.Exceptions; + using SysML2.NET.Extensions; [TestFixture] public class StateSubactionMembershipExtensionsTestFixture { [Test] - public void ComputeAction_ThrowsNotSupportedException() + public void VerifyComputeAction() { - Assert.That(() => ((IStateSubactionMembership)null).ComputeAction(), Throws.TypeOf()); + Assert.That(() => ((IStateSubactionMembership)null).ComputeAction(), Throws.TypeOf()); + + // Empty OwnedRelatedElement → [1..1] violation: throws IncompleteModelException. + var stateSubactionMembership = new StateSubactionMembership(); + + Assert.That(() => stateSubactionMembership.ComputeAction(), Throws.TypeOf()); + + // Single IActionUsage wired via the public API → returned. + var owningType = new StateDefinition(); + var singleMembership = new StateSubactionMembership { Kind = StateSubactionKind.Do }; + var singleAction = new ActionUsage(); + + owningType.AssignOwnership(singleMembership, singleAction); + + Assert.That(singleMembership.ComputeAction(), Is.SameAs(singleAction)); + + // Two IActionUsages in OwnedRelatedElement → [1..1] violation: throws IncompleteModelException. + var twoActionMembership = new StateSubactionMembership(); + var firstAction = new ActionUsage(); + var secondAction = new ActionUsage(); + + ((IContainedRelationship)twoActionMembership).OwnedRelatedElement.Add(firstAction); + ((IContainedRelationship)twoActionMembership).OwnedRelatedElement.Add(secondAction); + + Assert.That(() => twoActionMembership.ComputeAction(), Throws.TypeOf()); + + // Mixed-type owned related elements: exactly one IActionUsage alongside a non-IActionUsage (Namespace). + // The OfType() projection MUST pick out the IActionUsage regardless of its position + // (this is the core robustness guarantee — never positionally index the unfiltered collection). + var mixedMembership = new StateSubactionMembership(); + var siblingNonAction = new Namespace(); + var mixedAction = new ActionUsage(); + + ((IContainedRelationship)mixedMembership).OwnedRelatedElement.Add(siblingNonAction); + ((IContainedRelationship)mixedMembership).OwnedRelatedElement.Add(mixedAction); + + Assert.That(mixedMembership.ComputeAction(), Is.SameAs(mixedAction)); + + // OwnedRelatedElement populated with non-IActionUsage element(s) only → no IActionUsage match: + // [1..1] violation, throws IncompleteModelException. + var nonActionMembership = new StateSubactionMembership(); + var nonActionElement = new Namespace(); + + ((IContainedRelationship)nonActionMembership).OwnedRelatedElement.Add(nonActionElement); + + Assert.That(() => nonActionMembership.ComputeAction(), Throws.TypeOf()); } } } diff --git a/SysML2.NET.Tests/Extend/StateUsageExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/StateUsageExtensionsTestFixture.cs index 02f6c48b..1aec56a8 100644 --- a/SysML2.NET.Tests/Extend/StateUsageExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/StateUsageExtensionsTestFixture.cs @@ -1,20 +1,20 @@ // ------------------------------------------------------------------------------------------------- // -// +// // 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. -// +// // // ------------------------------------------------------------------------------------------------ @@ -28,7 +28,6 @@ namespace SysML2.NET.Tests.Extend using SysML2.NET.Core.POCO.Core.Features; using SysML2.NET.Core.POCO.Core.Types; using SysML2.NET.Core.POCO.Kernel.Behaviors; - using SysML2.NET.Core.POCO.Root.Namespaces; using SysML2.NET.Core.POCO.Systems.Actions; using SysML2.NET.Core.POCO.Systems.States; using SysML2.NET.Core.Systems.States; @@ -54,17 +53,14 @@ public void VerifyComputeDoAction() Assert.That(stateUsageWithEntry.ComputeDoAction(), Is.Null); - // Matching kind: one StateSubactionMembership of kind Do → NotSupportedException until - // StateSubactionMembershipExtensions.ComputeAction stub is resolved. - // For Later: depends on StateSubactionMembershipExtensions.ComputeAction stub + // Matching kind: one StateSubactionMembership of kind Do → returns the wired ActionUsage. var stateUsageWithDo = new StateUsage(); var doAction = new ActionUsage(); stateUsageWithDo.AssignOwnership(new StateSubactionMembership { Kind = StateSubactionKind.Do }, doAction); - Assert.That(() => stateUsageWithDo.ComputeDoAction(), Throws.TypeOf()); + Assert.That(stateUsageWithDo.ComputeDoAction(), Is.SameAs(doAction)); - // All three kinds present → first matching Do membership's action is accessed → NotSupportedException. - // For Later: depends on StateSubactionMembershipExtensions.ComputeAction stub + // All three kinds present → the Kind filter picks the Do membership's action; Entry and Exit excluded. var stateUsageAllKinds = new StateUsage(); var allKindsEntry = new ActionUsage(); var allKindsDo = new ActionUsage(); @@ -73,7 +69,7 @@ public void VerifyComputeDoAction() stateUsageAllKinds.AssignOwnership(new StateSubactionMembership { Kind = StateSubactionKind.Do }, allKindsDo); stateUsageAllKinds.AssignOwnership(new StateSubactionMembership { Kind = StateSubactionKind.Exit }, allKindsExit); - Assert.That(() => stateUsageAllKinds.ComputeDoAction(), Throws.TypeOf()); + Assert.That(stateUsageAllKinds.ComputeDoAction(), Is.SameAs(allKindsDo)); } [Test] @@ -93,17 +89,14 @@ public void VerifyComputeEntryAction() Assert.That(stateUsageWithDo.ComputeEntryAction(), Is.Null); - // Matching kind: one StateSubactionMembership of kind Entry → NotSupportedException until - // StateSubactionMembershipExtensions.ComputeAction stub is resolved. - // For Later: depends on StateSubactionMembershipExtensions.ComputeAction stub + // Matching kind: one StateSubactionMembership of kind Entry → returns the wired ActionUsage. var stateUsageWithEntry = new StateUsage(); var entryAction = new ActionUsage(); stateUsageWithEntry.AssignOwnership(new StateSubactionMembership { Kind = StateSubactionKind.Entry }, entryAction); - Assert.That(() => stateUsageWithEntry.ComputeEntryAction(), Throws.TypeOf()); + Assert.That(stateUsageWithEntry.ComputeEntryAction(), Is.SameAs(entryAction)); - // All three kinds present → first matching Entry membership's action is accessed → NotSupportedException. - // For Later: depends on StateSubactionMembershipExtensions.ComputeAction stub + // All three kinds present → the Kind filter picks the Entry membership's action; Do and Exit excluded. var stateUsageAllKinds = new StateUsage(); var allKindsEntry = new ActionUsage(); var allKindsDo = new ActionUsage(); @@ -112,7 +105,7 @@ public void VerifyComputeEntryAction() stateUsageAllKinds.AssignOwnership(new StateSubactionMembership { Kind = StateSubactionKind.Do }, allKindsDo); stateUsageAllKinds.AssignOwnership(new StateSubactionMembership { Kind = StateSubactionKind.Exit }, allKindsExit); - Assert.That(() => stateUsageAllKinds.ComputeEntryAction(), Throws.TypeOf()); + Assert.That(stateUsageAllKinds.ComputeEntryAction(), Is.SameAs(allKindsEntry)); } [Test] @@ -132,17 +125,14 @@ public void VerifyComputeExitAction() Assert.That(stateUsageWithDo.ComputeExitAction(), Is.Null); - // Matching kind: one StateSubactionMembership of kind Exit → NotSupportedException until - // StateSubactionMembershipExtensions.ComputeAction stub is resolved. - // For Later: depends on StateSubactionMembershipExtensions.ComputeAction stub + // Matching kind: one StateSubactionMembership of kind Exit → returns the wired ActionUsage. var stateUsageWithExit = new StateUsage(); var exitAction = new ActionUsage(); stateUsageWithExit.AssignOwnership(new StateSubactionMembership { Kind = StateSubactionKind.Exit }, exitAction); - Assert.That(() => stateUsageWithExit.ComputeExitAction(), Throws.TypeOf()); + Assert.That(stateUsageWithExit.ComputeExitAction(), Is.SameAs(exitAction)); - // All three kinds present → first matching Exit membership's action is accessed → NotSupportedException. - // For Later: depends on StateSubactionMembershipExtensions.ComputeAction stub + // All three kinds present → the Kind filter picks the Exit membership's action; Entry and Do excluded. var stateUsageAllKinds = new StateUsage(); var allKindsEntry = new ActionUsage(); var allKindsDo = new ActionUsage(); @@ -151,54 +141,7 @@ public void VerifyComputeExitAction() stateUsageAllKinds.AssignOwnership(new StateSubactionMembership { Kind = StateSubactionKind.Do }, allKindsDo); stateUsageAllKinds.AssignOwnership(new StateSubactionMembership { Kind = StateSubactionKind.Exit }, allKindsExit); - Assert.That(() => stateUsageAllKinds.ComputeExitAction(), Throws.TypeOf()); - } - - [Test] - public void VerifyComputeStateDefinition() - { - Assert.That(() => ((IStateUsage)null).ComputeStateDefinition(), Throws.TypeOf()); - - // Empty: no FeatureTyping in OwnedRelationship → empty list. - var emptyStateUsage = new StateUsage(); - - Assert.That(emptyStateUsage.ComputeStateDefinition(), Has.Count.EqualTo(0)); - - // One FeatureTyping whose Type is a StateDefinition (which implements IBehavior) → returned. - var stateUsageWithStateDefinition = new StateUsage(); - var stateDefinition = new StateDefinition(); - stateUsageWithStateDefinition.AssignOwnership(new FeatureTyping { Type = stateDefinition }); - - using (Assert.EnterMultipleScope()) - { - Assert.That(stateUsageWithStateDefinition.ComputeStateDefinition(), Has.Count.EqualTo(1)); - Assert.That(stateUsageWithStateDefinition.ComputeStateDefinition(), Does.Contain(stateDefinition)); - } - - // One FeatureTyping whose Type is a plain Behavior (not IStateDefinition) → also returned (spec allows it). - var stateUsageWithBehavior = new StateUsage(); - var plainBehavior = new Behavior(); - stateUsageWithBehavior.AssignOwnership(new FeatureTyping { Type = plainBehavior }); - - using (Assert.EnterMultipleScope()) - { - Assert.That(stateUsageWithBehavior.ComputeStateDefinition(), Has.Count.EqualTo(1)); - Assert.That(stateUsageWithBehavior.ComputeStateDefinition(), Does.Contain(plainBehavior)); - } - - // Mixed FeatureTypings: one IBehavior and one plain Classifier (not an IBehavior) → only the IBehavior returned. - var mixedStateUsage = new StateUsage(); - var mixedBehavior = new StateDefinition(); - var nonBehaviorType = new Classifier(); - mixedStateUsage.AssignOwnership(new FeatureTyping { Type = mixedBehavior }); - mixedStateUsage.AssignOwnership(new FeatureTyping { Type = nonBehaviorType }); - - using (Assert.EnterMultipleScope()) - { - Assert.That(mixedStateUsage.ComputeStateDefinition(), Has.Count.EqualTo(1)); - Assert.That(mixedStateUsage.ComputeStateDefinition(), Does.Contain(mixedBehavior)); - Assert.That(mixedStateUsage.ComputeStateDefinition(), Does.Not.Contain(nonBehaviorType)); - } + Assert.That(stateUsageAllKinds.ComputeExitAction(), Is.SameAs(allKindsExit)); } [Test] @@ -254,5 +197,52 @@ public void VerifyComputeIsSubstateUsageOperation() Assert.That(substateUnderOtherType.ComputeIsSubstateUsageOperation(false), Is.False); } + + [Test] + public void VerifyComputeStateDefinition() + { + Assert.That(() => ((IStateUsage)null).ComputeStateDefinition(), Throws.TypeOf()); + + // Empty: no FeatureTyping in OwnedRelationship → empty list. + var emptyStateUsage = new StateUsage(); + + Assert.That(emptyStateUsage.ComputeStateDefinition(), Has.Count.EqualTo(0)); + + // One FeatureTyping whose Type is a StateDefinition (which implements IBehavior) → returned. + var stateUsageWithStateDefinition = new StateUsage(); + var stateDefinition = new StateDefinition(); + stateUsageWithStateDefinition.AssignOwnership(new FeatureTyping { Type = stateDefinition }); + + using (Assert.EnterMultipleScope()) + { + Assert.That(stateUsageWithStateDefinition.ComputeStateDefinition(), Has.Count.EqualTo(1)); + Assert.That(stateUsageWithStateDefinition.ComputeStateDefinition(), Does.Contain(stateDefinition)); + } + + // One FeatureTyping whose Type is a plain Behavior (not IStateDefinition) → also returned (spec allows it). + var stateUsageWithBehavior = new StateUsage(); + var plainBehavior = new Behavior(); + stateUsageWithBehavior.AssignOwnership(new FeatureTyping { Type = plainBehavior }); + + using (Assert.EnterMultipleScope()) + { + Assert.That(stateUsageWithBehavior.ComputeStateDefinition(), Has.Count.EqualTo(1)); + Assert.That(stateUsageWithBehavior.ComputeStateDefinition(), Does.Contain(plainBehavior)); + } + + // Mixed FeatureTypings: one IBehavior and one plain Classifier (not an IBehavior) → only the IBehavior returned. + var mixedStateUsage = new StateUsage(); + var mixedBehavior = new StateDefinition(); + var nonBehaviorType = new Classifier(); + mixedStateUsage.AssignOwnership(new FeatureTyping { Type = mixedBehavior }); + mixedStateUsage.AssignOwnership(new FeatureTyping { Type = nonBehaviorType }); + + using (Assert.EnterMultipleScope()) + { + Assert.That(mixedStateUsage.ComputeStateDefinition(), Has.Count.EqualTo(1)); + Assert.That(mixedStateUsage.ComputeStateDefinition(), Does.Contain(mixedBehavior)); + Assert.That(mixedStateUsage.ComputeStateDefinition(), Does.Not.Contain(nonBehaviorType)); + } + } } } diff --git a/SysML2.NET/Extend/ObjectiveMembershipExtensions.cs b/SysML2.NET/Extend/ObjectiveMembershipExtensions.cs index ad169e0d..0ed53358 100644 --- a/SysML2.NET/Extend/ObjectiveMembershipExtensions.cs +++ b/SysML2.NET/Extend/ObjectiveMembershipExtensions.cs @@ -1,39 +1,33 @@ // ------------------------------------------------------------------------------------------------- // -// -// Copyright (C) 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 -// +// +// 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.Core.POCO.Systems.Cases { using System; - using System.Collections.Generic; - using SysML2.NET.Core.Root.Namespaces; - using SysML2.NET.Core.POCO.Core.Features; - using SysML2.NET.Core.POCO.Core.Types; - 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.Requirements; + using SysML2.NET.Extensions; /// - /// The class provides extensions methods for - /// the interface + /// The class provides extensions methods for + /// the interface /// internal static class ObjectiveMembershipExtensions { @@ -41,16 +35,19 @@ internal static class ObjectiveMembershipExtensions /// Computes the derived property. /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IRequirementUsage ComputeOwnedObjectiveRequirement(this IObjectiveMembership objectiveMembershipSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); - } + if (objectiveMembershipSubject == null) + { + throw new ArgumentNullException(nameof(objectiveMembershipSubject)); + } + return objectiveMembershipSubject.OwnedRelatedElement.RequireSingleOfType(nameof(objectiveMembershipSubject)); + } } } diff --git a/SysML2.NET/Extend/ResultExpressionMembershipExtensions.cs b/SysML2.NET/Extend/ResultExpressionMembershipExtensions.cs index 75880040..48f49e71 100644 --- a/SysML2.NET/Extend/ResultExpressionMembershipExtensions.cs +++ b/SysML2.NET/Extend/ResultExpressionMembershipExtensions.cs @@ -1,38 +1,32 @@ // ------------------------------------------------------------------------------------------------- // -// -// Copyright (C) 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 -// +// +// 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.Core.POCO.Kernel.Functions { using System; - using System.Collections.Generic; - using SysML2.NET.Core.Root.Namespaces; - using SysML2.NET.Core.POCO.Core.Features; - using SysML2.NET.Core.POCO.Core.Types; - using SysML2.NET.Core.POCO.Root.Annotations; - using SysML2.NET.Core.POCO.Root.Elements; - using SysML2.NET.Core.POCO.Root.Namespaces; + using SysML2.NET.Extensions; /// - /// The class provides extensions methods for - /// the interface + /// The class provides extensions methods for + /// the interface /// internal static class ResultExpressionMembershipExtensions { @@ -40,16 +34,19 @@ internal static class ResultExpressionMembershipExtensions /// Computes the derived property. /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IExpression ComputeOwnedResultExpression(this IResultExpressionMembership resultExpressionMembershipSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); - } + if (resultExpressionMembershipSubject == null) + { + throw new ArgumentNullException(nameof(resultExpressionMembershipSubject)); + } + return resultExpressionMembershipSubject.OwnedRelatedElement.RequireSingleOfType(nameof(resultExpressionMembershipSubject)); + } } } diff --git a/SysML2.NET/Extend/StakeholderMembershipExtensions.cs b/SysML2.NET/Extend/StakeholderMembershipExtensions.cs index 7741ba90..09e1c44f 100644 --- a/SysML2.NET/Extend/StakeholderMembershipExtensions.cs +++ b/SysML2.NET/Extend/StakeholderMembershipExtensions.cs @@ -1,41 +1,33 @@ // ------------------------------------------------------------------------------------------------- // -// -// Copyright (C) 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 -// +// +// 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.Core.POCO.Systems.Requirements { using System; - using System.Collections.Generic; - using SysML2.NET.Core.Core.Types; - using SysML2.NET.Core.Root.Namespaces; - using SysML2.NET.Core.POCO.Core.Features; - using SysML2.NET.Core.POCO.Core.Types; - using SysML2.NET.Core.POCO.Kernel.Behaviors; - 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.Parts; + using SysML2.NET.Extensions; /// - /// The class provides extensions methods for - /// the interface + /// The class provides extensions methods for + /// the interface /// internal static class StakeholderMembershipExtensions { @@ -43,16 +35,19 @@ internal static class StakeholderMembershipExtensions /// Computes the derived property. /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IPartUsage ComputeOwnedStakeholderParameter(this IStakeholderMembership stakeholderMembershipSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); - } + if (stakeholderMembershipSubject == null) + { + throw new ArgumentNullException(nameof(stakeholderMembershipSubject)); + } + return stakeholderMembershipSubject.OwnedRelatedElement.RequireSingleOfType(nameof(stakeholderMembershipSubject)); + } } } diff --git a/SysML2.NET/Extend/StateSubactionMembershipExtensions.cs b/SysML2.NET/Extend/StateSubactionMembershipExtensions.cs index 58206a71..eabdb191 100644 --- a/SysML2.NET/Extend/StateSubactionMembershipExtensions.cs +++ b/SysML2.NET/Extend/StateSubactionMembershipExtensions.cs @@ -1,40 +1,33 @@ // ------------------------------------------------------------------------------------------------- // -// -// Copyright (C) 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 -// +// +// 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.Core.POCO.Systems.States { using System; - using System.Collections.Generic; - using SysML2.NET.Core.Root.Namespaces; - using SysML2.NET.Core.Systems.States; - using SysML2.NET.Core.POCO.Core.Features; - using SysML2.NET.Core.POCO.Core.Types; - 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.Extensions; /// - /// The class provides extensions methods for - /// the interface + /// The class provides extensions methods for + /// the interface /// internal static class StateSubactionMembershipExtensions { @@ -42,16 +35,19 @@ internal static class StateSubactionMembershipExtensions /// Computes the derived property. /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IActionUsage ComputeAction(this IStateSubactionMembership stateSubactionMembershipSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); - } + if (stateSubactionMembershipSubject == null) + { + throw new ArgumentNullException(nameof(stateSubactionMembershipSubject)); + } + return stateSubactionMembershipSubject.OwnedRelatedElement.RequireSingleOfType(nameof(stateSubactionMembershipSubject)); + } } }