diff --git a/.claude/commands/implement-extensions.md b/.claude/commands/implement-extensions.md index 73ff90a8c..424605531 100644 --- a/.claude/commands/implement-extensions.md +++ b/.claude/commands/implement-extensions.md @@ -58,8 +58,19 @@ From `$ARGUMENTS = C:\CODE\SysML2.NET\SysML2.NET\Extend\Extensions.cs`: - **Subject param name**: lowercase first char of `` + `[1..]` + `Subject` (e.g. `Type` → `typeSubject`, `Feature` → `featureSubject`). - **Notes file**: `C:\CODE\SysML2.NET\.team-notes\-extensions-spec.md` (kebab-case ``). Create the `.team-notes\` directory if it doesn't exist (`mkdir -p`). - **Team name**: `-extensions-impl`. - -Print the derived paths back to the user as a sanity check. +- **GitHub issue number**: discover via + ```bash + gh issue list --repo STARIONGROUP/SysML2.NET --state all \ + --search "SysML2.NET/Extend/Extensions.cs in:body" \ + --json number,title,state --limit 5 + ``` + Pick the single match. If 0 results or >1 results, surface to the user and + ask for an explicit issue number before proceeding (do not guess). + Search-by-body is preferred over search-by-title because the body always + contains the canonical source-path string and is therefore unambiguous. +- **Issue URL**: `https://github.com/STARIONGROUP/SysML2.NET/issues/`. + +Print the derived paths (including the issue URL) back to the user as a sanity check. ### 3. Enumerate stub methods @@ -298,9 +309,70 @@ Report to the user: consider a follow-up issue"). - Spec-text-only methods (e.g. `IsConjugated`) — flag separately so the user knows the implementation is grounded in spec prose rather than OCL. +- **Issue checklist sync**: `` — `` newly ticked, + `` newly added, `/` total (filled in after step 11). Do NOT auto-commit. The user reviews and commits. +### 11. Sync GitHub issue checklist + +Runs **unconditionally** after step 10, even on reviewer NEEDS FIX. Rationale: +the issue should always reflect the current implementation state of the file; +unresolved findings are separately surfaced in the final-summary report. The +`gh issue edit` push must touch ONLY the `### Checklist` section. + +1. **Fetch current issue body**: + ```bash + gh issue view --repo STARIONGROUP/SysML2.NET --json body -q .body > + ``` + Read the file with the Read tool. Locate the `### Checklist` section + (everything between that header and the next `### ` header or EOF). + +2. **Enumerate implementation status from the production file**. For each + `Compute*` method declared in `{{PRODUCTION_FILE}}`: + - Extract the full extension-method signature in the same format the + existing checklist uses (return type + method name + `(this [, …])`), + e.g. `List ComputeDefinition(this IUsage)`. + - Mark **implemented** when BOTH of the following hold: + - The method body does not contain `throw new NotSupportedException`. + - The targeted-fixture test for it (`Verify{MethodName}` in + `{{TEST_FILE}}`) passed in step 7's last `dotnet test` run. Use the + output already captured; do NOT re-run tests. + +3. **Compute the new Checklist section**: + - For each existing checklist item: tick it (`- [x]`) if the corresponding + method is implemented per (2); otherwise leave its current state. + - For each `Compute*` method in `{{PRODUCTION_FILE}}` whose signature is + not present in the checklist (use exact-string match on the back-tick + content), append it as a new line. Tick it iff implemented. + - Preserve the relative order of pre-existing items. Append new items + after the last existing item, in declaration order from the production + file. + - Do NOT remove any existing item, even if its signature no longer matches + a method in the production file (signature drift is the user's call). + +4. **Stitch the new body**: replace ONLY the lines under `### Checklist` + (up to the next `### ` header or EOF) with the recomputed block. + Everything else — `### Prerequisites`, `### Description`, + `### System Configuration`, blank lines, trailing whitespace — is + preserved verbatim. Use a small string-slice (not a regex rewrite of the + whole body) to minimize the diff. + +5. **Push the update**: + ```bash + gh issue edit --repo STARIONGROUP/SysML2.NET --body-file + ``` + Use `--body-file` (not `--body "..."`) so multi-line content survives the + shell unchanged. + +6. **Verify**: re-fetch with `gh issue view` and diff against the version you + pushed. If the diff is non-empty outside the Checklist section, abort and + surface to the user — do NOT push a second time. + +7. **Report** back into the step-10 final-summary line: issue URL, count of + newly ticked items, count of newly added items, and the resulting + `/` ratio. + ## Notes for the orchestrator (you, the main agent) - Pick the model per role using the complexity-grading rubric in step 3.5. @@ -326,3 +398,8 @@ Do NOT auto-commit. The user reviews and commits. - If a build error involves an explicit-interface-impl loop (e.g. `(INamespace)x` cast not bypassing virtual dispatch), call the static extension method directly rather than via interface dispatch — pattern from the TypeExtensions task fix. +- The step-11 GitHub-issue sync runs **unconditionally** after step 10, even on + reviewer NEEDS FIX. Rationale: the issue should always reflect the current + implementation state of the file; unresolved findings are separately surfaced + in the final-summary report. The `gh issue edit` push must touch ONLY the + `### Checklist` section — verify with a re-fetch + diff before reporting "done". diff --git a/.github/workflows/nuget-reference-check.yml b/.github/workflows/nuget-reference-check.yml index f56fc2ef0..224ce2dfb 100644 --- a/.github/workflows/nuget-reference-check.yml +++ b/.github/workflows/nuget-reference-check.yml @@ -20,7 +20,7 @@ jobs: - name: Setup .NET Environment uses: actions/setup-dotnet@v5.1.0 with: - dotnet-version: '10.0.107' + dotnet-version: '10.0.x' - name: Check for outdated packages id: outdated diff --git a/SysML2.NET.Serializer.TextualNotation.Tests/Writers/TextualNotationBuilderTestFixture.cs b/SysML2.NET.Serializer.TextualNotation.Tests/Writers/TextualNotationBuilderTestFixture.cs index b3db7e66d..2374f6cc2 100644 --- a/SysML2.NET.Serializer.TextualNotation.Tests/Writers/TextualNotationBuilderTestFixture.cs +++ b/SysML2.NET.Serializer.TextualNotation.Tests/Writers/TextualNotationBuilderTestFixture.cs @@ -109,6 +109,8 @@ public void Verify_that_textual_notation_is_produced_from_Quantities_root_namesp Assert.That(textualNotation, Does.Contain("private import ScalarValues::NumericalValue")); Assert.That(textualNotation, Does.Contain("isBound: Boolean")); Assert.That(textualNotation, Does.Contain(":>> elements")); + Assert.That(textualNotation, Does.Contain("assert constraint orderSum")); + Assert.That(textualNotation, Does.Contain("assert constraint boundMatch")); }); } } diff --git a/SysML2.NET.Serializer.TextualNotation/Writers/TextualNotationValidationExtensions.cs b/SysML2.NET.Serializer.TextualNotation/Writers/TextualNotationValidationExtensions.cs index 4f00d871c..bd3567893 100644 --- a/SysML2.NET.Serializer.TextualNotation/Writers/TextualNotationValidationExtensions.cs +++ b/SysML2.NET.Serializer.TextualNotation/Writers/TextualNotationValidationExtensions.cs @@ -208,15 +208,37 @@ private static IElement QueryCurrentOwnedRelationship(IElement element, TextualN /// all inherit transitively (PerformActionUsage/ExhibitStateUsage/IncludeUseCaseUsage are /// descendants, AssertConstraintUsage inherits , /// SatisfyRequirementUsage inherits ). + /// Several other descendants are NOT BehaviorUsageElement + /// alternatives — they belong to the sibling ActionNodeMember rule + /// (, , , + /// , , + /// , ) or to the + /// TransitionUsage / TargetTransitionUsage rules + /// (). Without the explicit exclusions below, a membership + /// owning e.g. a would be silently mis-routed to + /// BuildBehaviorUsageMember by the dispatchers at + /// FeatureMembershipTextualNotationBuilder.cs:271 and + /// TypeTextualNotationBuilder.cs:289/318, emitting the literal action keyword + /// instead of the expected ActionNode keyword (merge, fork, decision, + /// join, send, accept, assign, terminate, if, + /// while, for) or the transition keyword. /// /// The /// The active (unused for this guard) - /// True if the membership owns a behavior-usage element + /// True if the membership owns a behavior-usage element (and that element is not routed by a more specific sibling rule) internal static bool IsValidForBehaviorUsageMember(this IFeatureMembership featureMembership, TextualNotationWriterContext writerContext) { return featureMembership?.OwnedRelatedElement.Any(element => - element is IActionUsage or IStateUsage or IConstraintUsage - or IRequirementUsage or ICaseUsage) == true; + (element is IActionUsage or IStateUsage or IConstraintUsage + or IRequirementUsage or ICaseUsage) + && element is not IControlNode + && element is not ISendActionUsage + && element is not IAcceptActionUsage + && element is not IAssignmentActionUsage + && element is not ITerminateActionUsage + && element is not IIfActionUsage + && element is not ILoopActionUsage + && element is not ITransitionUsage) == true; } /// @@ -270,15 +292,29 @@ internal static bool IsValidForBinaryInterfacePart(this IInterfaceUsage interfac /// Matches any structural-usage metaclass. IndividualUsage/PortionUsage/EventOccurrenceUsage /// inherit ; Message and SuccessionFlowUsage inherit /// . + /// The BehaviorUsageElement subtypes (, + /// and their descendants) ALSO inherit via the metaclass hierarchy + /// (e.g. IConstraintUsage : IOccurrenceUsage, IActionUsage : IOccurrenceUsage), + /// so the bare is IOccurrenceUsage would silently pull them in. They must be explicitly + /// excluded so the upstream dispatcher (BuildOccurrenceUsageElement) routes them to + /// BuildBehaviorUsageElement instead — without this exclusion they fall through the + /// BuildStructureUsageElement switch to the bare case IOccurrenceUsage → BuildPortionUsage + /// arm, which mis-renders constraints as #name;. + /// is itself (per + /// IFlowUsage : IConnectorAsUsage, IFlow, IActionUsage), but it is a + /// StructureUsageElement alternative, so the IActionUsage exclusion makes an explicit + /// IFlowUsage exception. /// /// The /// The active (unused for this guard) - /// True if the usage is a structural-usage metaclass + /// True if the usage is a structural-usage metaclass (and not a behavior-usage subtype routed by a sibling rule) internal static bool IsValidForStructureUsageElement(this IUsage usage, TextualNotationWriterContext writerContext) { - return usage is IOccurrenceUsage or IItemUsage or IPartUsage or IViewUsage - or IRenderingUsage or IPortUsage or IConnectionUsage or IInterfaceUsage - or IAllocationUsage or IFlowUsage; + return (usage is IOccurrenceUsage or IItemUsage or IPartUsage or IViewUsage + or IRenderingUsage or IPortUsage or IConnectionUsage or IInterfaceUsage + or IAllocationUsage or IFlowUsage) + && usage is not IConstraintUsage + && (usage is not IActionUsage || usage is IFlowUsage); } /// @@ -286,13 +322,24 @@ or IRenderingUsage or IPortUsage or IConnectionUsage or IInterfaceUsage /// OccurrenceUsage = OccurrenceUsagePrefix 'occurrence' Usage — the general case /// where neither isIndividual nor portionKind has been set by one of the more /// specific rules (IndividualUsage, PortionUsage). + /// The bare-keyword form must NOT match runtime types covered by sibling rules in + /// StructureUsageElement (, , + /// ) or BehaviorUsageElement + /// (, ) — each of those has its + /// own dedicated grammar rule and dispatch arm. Without these exclusions, a constraint + /// or action would be silently rendered as occurrence name;. /// /// The /// The active (unused for this guard) - /// True if the usage is a plain occurrence (no individual, no portion kind) + /// True if the usage is a plain occurrence (no individual, no portion kind, and not routed by a more specific sibling rule) internal static bool IsValidForOccurrenceUsage(this IOccurrenceUsage occurrenceUsage, TextualNotationWriterContext writerContext) { - return occurrenceUsage is { IsIndividual: false, PortionKind: null }; + return occurrenceUsage is { IsIndividual: false, PortionKind: null } + && occurrenceUsage is not IItemUsage + && occurrenceUsage is not IPortUsage + && occurrenceUsage is not IActionUsage + && occurrenceUsage is not IConstraintUsage + && occurrenceUsage is not IEventOccurrenceUsage; } /// diff --git a/SysML2.NET.Tests/Extend/UsageExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/UsageExtensionsTestFixture.cs index ffbf16682..a3d3f56bc 100644 --- a/SysML2.NET.Tests/Extend/UsageExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/UsageExtensionsTestFixture.cs @@ -1,249 +1,838 @@ // ------------------------------------------------------------------------------------------------- -// -// +// +// // 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.Core.Types; + using SysML2.NET.Core.POCO.Core.Features; + using SysML2.NET.Core.POCO.Core.Types; + using SysML2.NET.Core.POCO.Root.Elements; + using SysML2.NET.Core.POCO.Systems.Actions; + using SysML2.NET.Core.POCO.Systems.Allocations; + using SysML2.NET.Core.POCO.Systems.AnalysisCases; + using SysML2.NET.Core.POCO.Systems.Attributes; + using SysML2.NET.Core.POCO.Systems.Calculations; + using SysML2.NET.Core.POCO.Systems.Cases; + using SysML2.NET.Core.POCO.Systems.Connections; + using SysML2.NET.Core.POCO.Systems.Constraints; using SysML2.NET.Core.POCO.Systems.DefinitionAndUsage; + using SysML2.NET.Core.POCO.Systems.Enumerations; + using SysML2.NET.Core.POCO.Systems.Flows; + using SysML2.NET.Core.POCO.Systems.Interfaces; + using SysML2.NET.Core.POCO.Systems.Items; + using SysML2.NET.Core.POCO.Systems.Metadata; + using SysML2.NET.Core.POCO.Systems.Occurrences; + using SysML2.NET.Core.POCO.Systems.Parts; + using SysML2.NET.Core.POCO.Systems.Ports; + using SysML2.NET.Core.POCO.Systems.Requirements; + using SysML2.NET.Core.POCO.Systems.States; + using SysML2.NET.Core.POCO.Systems.UseCases; + using SysML2.NET.Core.POCO.Systems.VerificationCases; + using SysML2.NET.Core.POCO.Systems.Views; + using SysML2.NET.Extensions; [TestFixture] public class UsageExtensionsTestFixture { + /// + /// Builds a Usage owning one of every Usage subkind referenced by the + /// nested-* derived selectors, and returns the (subject, kindMap) + /// for use across the 23 ComputeNestedXxx tests. Each subkind is + /// wired via a FeatureMembership in OwnedRelationship. + /// + private static Usage BuildUsageWithMixedNestedKinds(out NestedKindBag bag) + { + var subject = new Usage(); + + bag = new NestedKindBag + { + Action = new ActionUsage(), + Allocation = new AllocationUsage(), + AnalysisCase = new AnalysisCaseUsage(), + Attribute = new AttributeUsage(), + Calculation = new CalculationUsage(), + Case = new CaseUsage(), + Concern = new ConcernUsage(), + Connection = new BindingConnectorAsUsage(), + Constraint = new ConstraintUsage(), + Enumeration = new EnumerationUsage(), + Flow = new FlowUsage(), + Interface = new InterfaceUsage(), + Item = new ItemUsage(), + Metadata = new MetadataUsage(), + Occurrence = new OccurrenceUsage(), + Part = new PartUsage(), + Port = new PortUsage(), + Reference = new ReferenceUsage(), + Rendering = new RenderingUsage(), + Requirement = new RequirementUsage(), + State = new StateUsage(), + Transition = new TransitionUsage(), + UseCase = new UseCaseUsage(), + VerificationCase = new VerificationCaseUsage(), + View = new ViewUsage(), + Viewpoint = new ViewpointUsage(), + BareUsage = new Usage(), + }; + + subject.AssignOwnership(new FeatureMembership(), bag.Action); + subject.AssignOwnership(new FeatureMembership(), bag.Allocation); + subject.AssignOwnership(new FeatureMembership(), bag.AnalysisCase); + subject.AssignOwnership(new FeatureMembership(), bag.Attribute); + subject.AssignOwnership(new FeatureMembership(), bag.Calculation); + subject.AssignOwnership(new FeatureMembership(), bag.Case); + subject.AssignOwnership(new FeatureMembership(), bag.Concern); + subject.AssignOwnership(new FeatureMembership(), bag.Connection); + subject.AssignOwnership(new FeatureMembership(), bag.Constraint); + subject.AssignOwnership(new FeatureMembership(), bag.Enumeration); + subject.AssignOwnership(new FeatureMembership(), bag.Flow); + subject.AssignOwnership(new FeatureMembership(), bag.Interface); + subject.AssignOwnership(new FeatureMembership(), bag.Item); + subject.AssignOwnership(new FeatureMembership(), bag.Metadata); + subject.AssignOwnership(new FeatureMembership(), bag.Occurrence); + subject.AssignOwnership(new FeatureMembership(), bag.Part); + subject.AssignOwnership(new FeatureMembership(), bag.Port); + subject.AssignOwnership(new FeatureMembership(), bag.Reference); + subject.AssignOwnership(new FeatureMembership(), bag.Rendering); + subject.AssignOwnership(new FeatureMembership(), bag.Requirement); + subject.AssignOwnership(new FeatureMembership(), bag.State); + subject.AssignOwnership(new FeatureMembership(), bag.Transition); + subject.AssignOwnership(new FeatureMembership(), bag.UseCase); + subject.AssignOwnership(new FeatureMembership(), bag.VerificationCase); + subject.AssignOwnership(new FeatureMembership(), bag.View); + subject.AssignOwnership(new FeatureMembership(), bag.Viewpoint); + subject.AssignOwnership(new FeatureMembership(), bag.BareUsage); + + return subject; + } + + private sealed class NestedKindBag + { + public ActionUsage Action; + public AllocationUsage Allocation; + public AnalysisCaseUsage AnalysisCase; + public AttributeUsage Attribute; + public CalculationUsage Calculation; + public CaseUsage Case; + public ConcernUsage Concern; + public BindingConnectorAsUsage Connection; + public ConstraintUsage Constraint; + public EnumerationUsage Enumeration; + public FlowUsage Flow; + public InterfaceUsage Interface; + public ItemUsage Item; + public MetadataUsage Metadata; + public OccurrenceUsage Occurrence; + public PartUsage Part; + public PortUsage Port; + public ReferenceUsage Reference; + public RenderingUsage Rendering; + public RequirementUsage Requirement; + public StateUsage State; + public TransitionUsage Transition; + public UseCaseUsage UseCase; + public VerificationCaseUsage VerificationCase; + public ViewUsage View; + public ViewpointUsage Viewpoint; + public Usage BareUsage; + } + [Test] - public void ComputeDefinition_ThrowsNotSupportedException() + public void VerifyComputeDefinition() { - Assert.That(() => ((IUsage)null).ComputeDefinition(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeDefinition(), Throws.TypeOf()); + + var subject = new Usage(); + + Assert.That(subject.ComputeDefinition(), Has.Count.EqualTo(0)); + + var definition = new Definition(); + var bareType = new SysML2.NET.Core.POCO.Core.Types.Type(); + subject.AssignOwnership(new FeatureTyping { Type = definition }); + subject.AssignOwnership(new FeatureTyping { Type = bareType }); + + // Definition is an IClassifier; bare Type is not — only the Definition is returned. + Assert.That(subject.ComputeDefinition(), Is.EquivalentTo(new[] { definition })); } - + [Test] - public void ComputeDirectedUsage_ThrowsNotSupportedException() + public void VerifyComputeDirectedUsage() { - Assert.That(() => ((IUsage)null).ComputeDirectedUsage(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeDirectedUsage(), Throws.TypeOf()); + + var subject = new Usage(); + + Assert.That(subject.ComputeDirectedUsage(), Has.Count.EqualTo(0)); + + var directedUsage = new PartUsage { Direction = FeatureDirectionKind.In }; + var directedFeature = new Feature { Direction = FeatureDirectionKind.Out }; + var undirectedUsage = new PartUsage(); + + subject.AssignOwnership(new FeatureMembership(), directedUsage); + subject.AssignOwnership(new FeatureMembership(), directedFeature); + subject.AssignOwnership(new FeatureMembership(), undirectedUsage); + + // Only the directed Usage is returned (directed non-Usage Feature and undirected Usage are excluded). + Assert.That(subject.ComputeDirectedUsage(), Is.EquivalentTo(new[] { directedUsage })); } - + [Test] - public void ComputeIsReference_ThrowsNotSupportedException() + public void VerifyComputeIsReference() { - Assert.That(() => ((IUsage)null).ComputeIsReference(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeIsReference(), Throws.TypeOf()); + + var subject = new Usage(); + + using (Assert.EnterMultipleScope()) + { + Assert.That(subject.ComputeIsReference(), Is.True); + + subject.IsComposite = true; + + Assert.That(subject.ComputeIsReference(), Is.False); + } } - + [Test] - public void ComputeMayTimeVary_ThrowsNotSupportedException() + public void VerifyComputeMayTimeVary() { - Assert.That(() => ((IUsage)null).ComputeMayTimeVary(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeMayTimeVary(), Throws.TypeOf()); + + // Negative path: no owningType → false + var orphan = new Usage(); + + Assert.That(orphan.ComputeMayTimeVary(), Is.False); + + // Negative path: owningType present, but does NOT specialize Occurrences::Occurrence → false + var owner = new Definition(); + var subject = new Usage(); + owner.AssignOwnership(new FeatureMembership(), subject); + + Assert.That(subject.ComputeMayTimeVary(), Is.False); + + // The "true" branch and the IsPortion / SelfLink / HappensLink / Action negative + // branches all require a SysML library bootstrap (Occurrences::Occurrence, + // Links::SelfLink, Occurrences::HappensLink, Actions::Action) which is out of scope + // for this fixture. Negative path on owningType-not-Occurrence is the safe assertion + // that exercises the OCL chain without needing a wired library. } - + [Test] - public void ComputeNestedAction_ThrowsNotSupportedException() + public void VerifyComputeNestedAction() { - Assert.That(() => ((IUsage)null).ComputeNestedAction(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedAction(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedAction(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedAction(), Does.Contain(bag.Action)); + Assert.That(subject.ComputeNestedAction(), Does.Not.Contain(bag.BareUsage)); + Assert.That(subject.ComputeNestedAction(), Does.Not.Contain(bag.Attribute)); } - + [Test] - public void ComputeNestedAllocation_ThrowsNotSupportedException() + public void VerifyComputeNestedAllocation() { - Assert.That(() => ((IUsage)null).ComputeNestedAllocation(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedAllocation(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedAllocation(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedAllocation(), Does.Contain(bag.Allocation)); + Assert.That(subject.ComputeNestedAllocation(), Does.Not.Contain(bag.BareUsage)); + } + + [Test] + public void VerifyComputeNestedAnalysisCase() + { + Assert.That(() => ((IUsage)null).ComputeNestedAnalysisCase(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedAnalysisCase(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedAnalysisCase(), Does.Contain(bag.AnalysisCase)); + Assert.That(subject.ComputeNestedAnalysisCase(), Does.Not.Contain(bag.BareUsage)); + } + + [Test] + public void VerifyComputeNestedAttribute() + { + Assert.That(() => ((IUsage)null).ComputeNestedAttribute(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedAttribute(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedAttribute(), Does.Contain(bag.Attribute)); + Assert.That(subject.ComputeNestedAttribute(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedAnalysisCase_ThrowsNotSupportedException() + public void VerifyComputeNestedCalculation() { - Assert.That(() => ((IUsage)null).ComputeNestedAnalysisCase(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedCalculation(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedCalculation(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedCalculation(), Does.Contain(bag.Calculation)); + Assert.That(subject.ComputeNestedCalculation(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedAttribute_ThrowsNotSupportedException() + public void VerifyComputeNestedCase() { - Assert.That(() => ((IUsage)null).ComputeNestedAttribute(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedCase(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedCase(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedCase(), Does.Contain(bag.Case)); + Assert.That(subject.ComputeNestedCase(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedCalculation_ThrowsNotSupportedException() + public void VerifyComputeNestedConcern() { - Assert.That(() => ((IUsage)null).ComputeNestedCalculation(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedConcern(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedConcern(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedConcern(), Does.Contain(bag.Concern)); + Assert.That(subject.ComputeNestedConcern(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedCase_ThrowsNotSupportedException() + public void VerifyComputeNestedConnection() { - Assert.That(() => ((IUsage)null).ComputeNestedCase(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedConnection(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedConnection(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + // BindingConnectorAsUsage IS an IConnectorAsUsage. So is FlowUsage. + Assert.That(subject.ComputeNestedConnection(), Does.Contain(bag.Connection)); + Assert.That(subject.ComputeNestedConnection(), Does.Contain(bag.Flow)); + Assert.That(subject.ComputeNestedConnection(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedConcern_ThrowsNotSupportedException() + public void VerifyComputeNestedConstraint() { - Assert.That(() => ((IUsage)null).ComputeNestedConcern(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedConstraint(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedConstraint(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + // Requirement IS a Constraint, so both should appear. + Assert.That(subject.ComputeNestedConstraint(), Does.Contain(bag.Constraint)); + Assert.That(subject.ComputeNestedConstraint(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedConnection_ThrowsNotSupportedException() + public void VerifyComputeNestedEnumeration() { - Assert.That(() => ((IUsage)null).ComputeNestedConnection(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedEnumeration(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedEnumeration(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedEnumeration(), Does.Contain(bag.Enumeration)); + Assert.That(subject.ComputeNestedEnumeration(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedConstraint_ThrowsNotSupportedException() + public void VerifyComputeNestedFlow() { - Assert.That(() => ((IUsage)null).ComputeNestedConstraint(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedFlow(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedFlow(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedFlow(), Does.Contain(bag.Flow)); + Assert.That(subject.ComputeNestedFlow(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedEnumeration_ThrowsNotSupportedException() + public void VerifyComputeNestedInterface() { - Assert.That(() => ((IUsage)null).ComputeNestedEnumeration(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedInterface(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedInterface(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + // Asserts the C#-contract correction (XMI OCL erroneously says ReferenceUsage): + // ComputeNestedInterface returns IInterfaceUsage, NOT ReferenceUsage. + Assert.That(subject.ComputeNestedInterface(), Does.Contain(bag.Interface)); + Assert.That(subject.ComputeNestedInterface(), Does.Not.Contain(bag.Reference)); + Assert.That(subject.ComputeNestedInterface(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedFlow_ThrowsNotSupportedException() + public void VerifyComputeNestedItem() { - Assert.That(() => ((IUsage)null).ComputeNestedFlow(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedItem(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedItem(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + // PartUsage IS an ItemUsage, so it surfaces here too. + Assert.That(subject.ComputeNestedItem(), Does.Contain(bag.Item)); + Assert.That(subject.ComputeNestedItem(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedInterface_ThrowsNotSupportedException() + public void VerifyComputeNestedMetadata() { - Assert.That(() => ((IUsage)null).ComputeNestedInterface(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedMetadata(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedMetadata(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedMetadata(), Does.Contain(bag.Metadata)); + Assert.That(subject.ComputeNestedMetadata(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedItem_ThrowsNotSupportedException() + public void VerifyComputeNestedOccurrence() { - Assert.That(() => ((IUsage)null).ComputeNestedItem(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedOccurrence(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedOccurrence(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + // Many subkinds (ActionUsage, ItemUsage, PartUsage, PortUsage, ...) ARE OccurrenceUsage. + Assert.That(subject.ComputeNestedOccurrence(), Does.Contain(bag.Occurrence)); + Assert.That(subject.ComputeNestedOccurrence(), Does.Contain(bag.Action)); + Assert.That(subject.ComputeNestedOccurrence(), Does.Not.Contain(bag.BareUsage)); + Assert.That(subject.ComputeNestedOccurrence(), Does.Not.Contain(bag.Attribute)); } - + [Test] - public void ComputeNestedMetadata_ThrowsNotSupportedException() + public void VerifyComputeNestedPart() { - Assert.That(() => ((IUsage)null).ComputeNestedMetadata(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedPart(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedPart(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedPart(), Does.Contain(bag.Part)); + Assert.That(subject.ComputeNestedPart(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedOccurrence_ThrowsNotSupportedException() + public void VerifyComputeNestedPort() { - Assert.That(() => ((IUsage)null).ComputeNestedOccurrence(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedPort(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedPort(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedPort(), Does.Contain(bag.Port)); + Assert.That(subject.ComputeNestedPort(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedPart_ThrowsNotSupportedException() + public void VerifyComputeNestedReference() { - Assert.That(() => ((IUsage)null).ComputeNestedPart(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedReference(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedReference(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedReference(), Does.Contain(bag.Reference)); + Assert.That(subject.ComputeNestedReference(), Does.Not.Contain(bag.BareUsage)); + Assert.That(subject.ComputeNestedReference(), Does.Not.Contain(bag.Interface)); } - + [Test] - public void ComputeNestedPort_ThrowsNotSupportedException() + public void VerifyComputeNestedRendering() { - Assert.That(() => ((IUsage)null).ComputeNestedPort(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedRendering(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedRendering(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedRendering(), Does.Contain(bag.Rendering)); + Assert.That(subject.ComputeNestedRendering(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedReference_ThrowsNotSupportedException() + public void VerifyComputeNestedRequirement() { - Assert.That(() => ((IUsage)null).ComputeNestedReference(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedRequirement(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedRequirement(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedRequirement(), Does.Contain(bag.Requirement)); + Assert.That(subject.ComputeNestedRequirement(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedRendering_ThrowsNotSupportedException() + public void VerifyComputeNestedState() { - Assert.That(() => ((IUsage)null).ComputeNestedRendering(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedState(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedState(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedState(), Does.Contain(bag.State)); + Assert.That(subject.ComputeNestedState(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedRequirement_ThrowsNotSupportedException() + public void VerifyComputeNestedTransition() { - Assert.That(() => ((IUsage)null).ComputeNestedRequirement(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedTransition(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedTransition(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedTransition(), Does.Contain(bag.Transition)); + Assert.That(subject.ComputeNestedTransition(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedState_ThrowsNotSupportedException() + public void VerifyComputeNestedUsage() { - Assert.That(() => ((IUsage)null).ComputeNestedState(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedUsage(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedUsage(), Has.Count.EqualTo(0)); + + var subject = new Usage(); + var ownedUsage = new PartUsage(); + var ownedNonUsage = new Feature(); + + subject.AssignOwnership(new FeatureMembership(), ownedUsage); + subject.AssignOwnership(new FeatureMembership(), ownedNonUsage); + + // Only the IUsage member is returned; bare IFeature is excluded. + Assert.That(subject.ComputeNestedUsage(), Does.Contain(ownedUsage)); + Assert.That(subject.ComputeNestedUsage(), Does.Not.Contain(ownedNonUsage)); } - + [Test] - public void ComputeNestedTransition_ThrowsNotSupportedException() + public void VerifyComputeNestedUseCase() { - Assert.That(() => ((IUsage)null).ComputeNestedTransition(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedUseCase(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedUseCase(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedUseCase(), Does.Contain(bag.UseCase)); + Assert.That(subject.ComputeNestedUseCase(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedUsage_ThrowsNotSupportedException() + public void VerifyComputeNestedVerificationCase() { - Assert.That(() => ((IUsage)null).ComputeNestedUsage(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedVerificationCase(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedVerificationCase(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedVerificationCase(), Does.Contain(bag.VerificationCase)); + Assert.That(subject.ComputeNestedVerificationCase(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedUseCase_ThrowsNotSupportedException() + public void VerifyComputeNestedView() { - Assert.That(() => ((IUsage)null).ComputeNestedUseCase(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedView(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedView(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedView(), Does.Contain(bag.View)); + Assert.That(subject.ComputeNestedView(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedVerificationCase_ThrowsNotSupportedException() + public void VerifyComputeNestedViewpoint() { - Assert.That(() => ((IUsage)null).ComputeNestedVerificationCase(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeNestedViewpoint(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeNestedViewpoint(), Has.Count.EqualTo(0)); + + var subject = BuildUsageWithMixedNestedKinds(out var bag); + + Assert.That(subject.ComputeNestedViewpoint(), Does.Contain(bag.Viewpoint)); + Assert.That(subject.ComputeNestedViewpoint(), Does.Not.Contain(bag.BareUsage)); } - + [Test] - public void ComputeNestedView_ThrowsNotSupportedException() + public void VerifyComputeOwningDefinition() { - Assert.That(() => ((IUsage)null).ComputeNestedView(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeOwningDefinition(), Throws.TypeOf()); + + var orphan = new Usage(); + + Assert.That(orphan.ComputeOwningDefinition(), Is.Null); + + // Owner is a Definition → returned narrowed. + var definition = new Definition(); + var nestedInDefinition = new Usage(); + definition.AssignOwnership(new FeatureMembership(), nestedInDefinition); + + Assert.That(nestedInDefinition.ComputeOwningDefinition(), Is.SameAs(definition)); + + // Owner is a Usage (not Definition) → returns null. + var owningUsage = new Usage(); + var nestedInUsage = new Usage(); + owningUsage.AssignOwnership(new FeatureMembership(), nestedInUsage); + + Assert.That(nestedInUsage.ComputeOwningDefinition(), Is.Null); } - + [Test] - public void ComputeNestedViewpoint_ThrowsNotSupportedException() + public void VerifyComputeOwningUsage() { - Assert.That(() => ((IUsage)null).ComputeNestedViewpoint(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeOwningUsage(), Throws.TypeOf()); + + var orphan = new Usage(); + + Assert.That(orphan.ComputeOwningUsage(), Is.Null); + + // Owner is a Usage → returned narrowed. + var owningUsage = new Usage(); + var nestedInUsage = new Usage(); + owningUsage.AssignOwnership(new FeatureMembership(), nestedInUsage); + + Assert.That(nestedInUsage.ComputeOwningUsage(), Is.SameAs(owningUsage)); + + // Owner is a Definition (not Usage) → returns null. + var definition = new Definition(); + var nestedInDefinition = new Usage(); + definition.AssignOwnership(new FeatureMembership(), nestedInDefinition); + + Assert.That(nestedInDefinition.ComputeOwningUsage(), Is.Null); } - + [Test] - public void ComputeOwningDefinition_ThrowsNotSupportedException() + public void VerifyComputeUsage() { - Assert.That(() => ((IUsage)null).ComputeOwningDefinition(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeUsage(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeUsage(), Has.Count.EqualTo(0)); + + var subject = new Usage(); + var nestedUsage = new PartUsage(); + var nestedFeature = new Feature(); + + subject.AssignOwnership(new FeatureMembership(), nestedUsage); + subject.AssignOwnership(new FeatureMembership(), nestedFeature); + + // feature.OfType() — only the IUsage one surfaces. + Assert.That(subject.ComputeUsage(), Does.Contain(nestedUsage)); + Assert.That(subject.ComputeUsage(), Does.Not.Contain(nestedFeature)); } - + [Test] - public void ComputeOwningUsage_ThrowsNotSupportedException() + public void VerifyComputeVariant() { - Assert.That(() => ((IUsage)null).ComputeOwningUsage(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeVariant(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeVariant(), Has.Count.EqualTo(0)); + + // Populated case is stub-blocked: VariantMembershipExtensions.ComputeOwnedVariantUsage + // is still a NotSupportedException stub. Asserting NotSupportedException here makes the + // block visible; once the upstream stub is implemented, this assertion will fail loudly, + // forcing a real assertion to be written. + // For later: populated case depends on VariantMembershipExtensions.ComputeOwnedVariantUsage, + // which is still a stub. + var subject = new Usage(); + var variantUsage = new PartUsage(); + subject.AssignOwnership(new VariantMembership(), variantUsage); + + Assert.That(subject.ComputeVariant, Throws.TypeOf()); } - + [Test] - public void ComputeUsage_ThrowsNotSupportedException() + public void VerifyComputeVariantMembership() { - Assert.That(() => ((IUsage)null).ComputeUsage(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeVariantMembership(), Throws.TypeOf()); + + var emptySubject = new Usage(); + + Assert.That(emptySubject.ComputeVariantMembership(), Has.Count.EqualTo(0)); + + var subject = new Usage(); + var variantUsage = new PartUsage(); + var variantMembership = new VariantMembership(); + subject.AssignOwnership(variantMembership, variantUsage); + + // A non-VariantMembership FeatureMembership in the same OwnedRelationship MUST NOT surface. + var plainNested = new PartUsage(); + subject.AssignOwnership(new FeatureMembership(), plainNested); + + Assert.That(subject.ComputeVariantMembership(), Is.EquivalentTo(new[] { variantMembership })); } - + [Test] - public void ComputeVariant_ThrowsNotSupportedException() + public void VerifyComputeRedefinedNamingFeatureOperation() { - Assert.That(() => ((IUsage)null).ComputeVariant(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeRedefinedNamingFeatureOperation(), Throws.TypeOf()); + + // Branch 1: owningMembership is NOT a VariantMembership AND no Redefinition → null + var nonVariantOwner = new Definition(); + var subjectNonVariantNoRedef = new Usage(); + nonVariantOwner.AssignOwnership(new FeatureMembership(), subjectNonVariantNoRedef); + + Assert.That(subjectNonVariantNoRedef.ComputeRedefinedNamingFeatureOperation(), Is.Null); + + // Branch 1: owningMembership is NOT a VariantMembership AND has a Redefinition → returns RedefinedFeature + var nonVariantOwner2 = new Definition(); + var subjectNonVariantWithRedef = new Usage(); + nonVariantOwner2.AssignOwnership(new FeatureMembership(), subjectNonVariantWithRedef); + var redefinedTarget = new Feature(); + subjectNonVariantWithRedef.AssignOwnership(new Redefinition { RedefinedFeature = redefinedTarget }); + + Assert.That(subjectNonVariantWithRedef.ComputeRedefinedNamingFeatureOperation(), Is.SameAs(redefinedTarget)); + + // Branch 2: owningMembership IS a VariantMembership AND no ownedReferenceSubsetting → null + var variantOwner = new Usage(); + var variantSubject = new Usage(); + variantOwner.AssignOwnership(new VariantMembership(), variantSubject); + + Assert.That(variantSubject.ComputeRedefinedNamingFeatureOperation(), Is.Null); + + // Branch 2: owningMembership IS a VariantMembership AND ownedReferenceSubsetting present → returns ReferencedFeature + var variantOwner2 = new Usage(); + var variantSubjectWithRef = new Usage(); + variantOwner2.AssignOwnership(new VariantMembership(), variantSubjectWithRef); + var refTarget = new Feature(); + variantSubjectWithRef.AssignOwnership(new ReferenceSubsetting { ReferencedFeature = refTarget }); + + Assert.That(variantSubjectWithRef.ComputeRedefinedNamingFeatureOperation(), Is.SameAs(refTarget)); + + // Edge case: owningMembership is null → falls into branch 1 → null (no redefinitions). + var subjectNoOwner = new Usage(); + + Assert.That(subjectNoOwner.ComputeRedefinedNamingFeatureOperation(), Is.Null); } - + [Test] - public void ComputeVariantMembership_ThrowsNotSupportedException() + public void VerifyComputeReferencedFeatureTargetOperation() { - Assert.That(() => ((IUsage)null).ComputeVariantMembership(), Throws.TypeOf()); + Assert.That(() => ((IUsage)null).ComputeReferencedFeatureTargetOperation(), Throws.TypeOf()); + + // No ownedReferenceSubsetting → null + var subjectNoSubsetting = new Usage(); + + Assert.That(subjectNoSubsetting.ComputeReferencedFeatureTargetOperation(), Is.Null); + + // ownedReferenceSubsetting set, ReferencedFeature with no chainingFeatures → returns the referencedFeature itself + // (because IFeature.featureTarget == self when chainingFeatures is empty). + var subject = new Usage(); + var referencedFeature = new Feature(); + subject.AssignOwnership(new ReferenceSubsetting { ReferencedFeature = referencedFeature }); + + Assert.That(subject.ComputeReferencedFeatureTargetOperation(), Is.SameAs(referencedFeature)); } } } - diff --git a/SysML2.NET.Viewer.Tests/Services/Authentication/AuthenticationServiceTestFixture.cs b/SysML2.NET.Viewer.Tests/Services/Authentication/AuthenticationServiceTestFixture.cs index f986f3cb3..62b4bc8ae 100644 --- a/SysML2.NET.Viewer.Tests/Services/Authentication/AuthenticationServiceTestFixture.cs +++ b/SysML2.NET.Viewer.Tests/Services/Authentication/AuthenticationServiceTestFixture.cs @@ -24,8 +24,8 @@ namespace SysML2.NET.Viewer.Tests.Services.Authentication using System.Threading; using System.Threading.Tasks; - using Blazored.SessionStorage; - + using Microsoft.JSInterop; + using Moq; using NUnit.Framework; @@ -88,7 +88,7 @@ public async Task Verify_that_when_logout_session_storage_is_called_to_remove_se await this.authenticationService.Logout(cts.Token); - this.sessionStorageService.Verify(x => x.RemoveItemAsync(AnonymousAuthenticationStateProvider.SessionStorageKey, cts.Token), Times.Once); + this.sessionStorageService.Verify(x => x.RemoveItem(AnonymousAuthenticationStateProvider.SessionStorageKey), Times.Once); } } } diff --git a/SysML2.NET.Viewer.Tests/ViewModels/Components/LogoutViewModelTextFixture.cs b/SysML2.NET.Viewer.Tests/ViewModels/Components/LogoutViewModelTextFixture.cs index aacd17ba5..e5eeeb2c4 100644 --- a/SysML2.NET.Viewer.Tests/ViewModels/Components/LogoutViewModelTextFixture.cs +++ b/SysML2.NET.Viewer.Tests/ViewModels/Components/LogoutViewModelTextFixture.cs @@ -22,14 +22,13 @@ namespace SysML2.NET.Viewer.Tests.ViewModels.Components { using System.Threading.Tasks; - using Blazored.SessionStorage; - using Bunit; using Bunit.TestDoubles; - + using TestContext = Bunit.TestContext; using Microsoft.Extensions.DependencyInjection; + using Microsoft.JSInterop; using Moq; diff --git a/SysML2.NET.Viewer/Program.cs b/SysML2.NET.Viewer/Program.cs index f881b8844..d3c8a5ef3 100644 --- a/SysML2.NET.Viewer/Program.cs +++ b/SysML2.NET.Viewer/Program.cs @@ -24,7 +24,6 @@ namespace SysML2.NET.Viewer using System.Net.Http; using System.Reflection; using System.Threading.Tasks; - using Blazored.SessionStorage; using BlazorStrap; using Microsoft.AspNetCore.Components.Authorization; @@ -86,7 +85,7 @@ public static async Task Main(string[] args) /// The private static void AddServices(WebAssemblyHostBuilder builder) { - builder.Services.AddBlazoredSessionStorage(); + builder.Services.AddSessionStorageServices(); builder.Services.AddAuthorizationCore(); builder.Services.AddScoped(); diff --git a/SysML2.NET.Viewer/Services/Authentication/AnonymousAuthenticationStateProvider.cs b/SysML2.NET.Viewer/Services/Authentication/AnonymousAuthenticationStateProvider.cs index 74e617ede..bb35318d2 100644 --- a/SysML2.NET.Viewer/Services/Authentication/AnonymousAuthenticationStateProvider.cs +++ b/SysML2.NET.Viewer/Services/Authentication/AnonymousAuthenticationStateProvider.cs @@ -24,9 +24,8 @@ namespace SysML2.NET.Viewer.Services.Authentication using System.Security.Claims; using System.Threading.Tasks; - using Blazored.SessionStorage; - using Microsoft.AspNetCore.Components.Authorization; + using Microsoft.JSInterop; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -74,7 +73,7 @@ public AnonymousAuthenticationStateProvider(ISessionStorageService sessionStorag /// public override async Task GetAuthenticationStateAsync() { - var token = await this.sessionStorage.GetItemAsync(SessionStorageKey); + var token = this.sessionStorage.GetItem(SessionStorageKey); if (string.IsNullOrEmpty(token)) { diff --git a/SysML2.NET.Viewer/Services/Authentication/AuthenticationService.cs b/SysML2.NET.Viewer/Services/Authentication/AuthenticationService.cs index cf2511bdb..e90016c92 100644 --- a/SysML2.NET.Viewer/Services/Authentication/AuthenticationService.cs +++ b/SysML2.NET.Viewer/Services/Authentication/AuthenticationService.cs @@ -24,11 +24,10 @@ namespace SysML2.NET.Viewer.Services.Authentication using System.Threading; using System.Threading.Tasks; - using Blazored.SessionStorage; - using Microsoft.AspNetCore.Components.Authorization; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; + using Microsoft.JSInterop; using SySML2.NET.REST; @@ -107,7 +106,7 @@ public async Task Login(string username, string passwo { await this.restClient.Open(username, password, url, cancellationToken); - await this.sessionStorageService.SetItemAsync(AnonymousAuthenticationStateProvider.SessionStorageKey, username, cancellationToken); + this.sessionStorageService.SetItem(AnonymousAuthenticationStateProvider.SessionStorageKey, username); ((AnonymousAuthenticationStateProvider)this.authenticationStateProvider).NotifyAuthenticationStateChanged(); return AuthenticationStatusKind.Success; @@ -126,10 +125,11 @@ public async Task Login(string username, string passwo /// /// a /// - public async Task Logout(CancellationToken cancellationToken) + public Task Logout(CancellationToken cancellationToken) { - await this.sessionStorageService.RemoveItemAsync(AnonymousAuthenticationStateProvider.SessionStorageKey, cancellationToken); + this.sessionStorageService.RemoveItem(AnonymousAuthenticationStateProvider.SessionStorageKey); ((AnonymousAuthenticationStateProvider)this.authenticationStateProvider).NotifyAuthenticationStateChanged(); + return Task.CompletedTask; } } } diff --git a/SysML2.NET.Viewer/SysML2.NET.Viewer.csproj b/SysML2.NET.Viewer/SysML2.NET.Viewer.csproj index 8b0c7d245..56dcf989d 100644 --- a/SysML2.NET.Viewer/SysML2.NET.Viewer.csproj +++ b/SysML2.NET.Viewer/SysML2.NET.Viewer.csproj @@ -17,13 +17,13 @@ - + - + diff --git a/SysML2.NET/Extend/UsageExtensions.cs b/SysML2.NET/Extend/UsageExtensions.cs index 891c1fb4c..9cd8663bf 100644 --- a/SysML2.NET/Extend/UsageExtensions.cs +++ b/SysML2.NET/Extend/UsageExtensions.cs @@ -1,20 +1,20 @@ -// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- // -// -// 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. -// +// // // ------------------------------------------------------------------------------------------------ @@ -22,15 +22,10 @@ namespace SysML2.NET.Core.POCO.Systems.DefinitionAndUsage { using System; using System.Collections.Generic; + using System.Linq; - using SysML2.NET.Core.Core.Types; - using SysML2.NET.Core.Root.Namespaces; using SysML2.NET.Core.POCO.Core.Classifiers; 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.Core.POCO.Systems.Allocations; using SysML2.NET.Core.POCO.Systems.AnalysisCases; @@ -54,8 +49,8 @@ namespace SysML2.NET.Core.POCO.Systems.DefinitionAndUsage using SysML2.NET.Core.POCO.Systems.Views; /// - /// The class provides extensions methods for - /// the interface + /// The class provides extensions methods for + /// the interface /// internal static class UsageExtensions { @@ -63,15 +58,20 @@ internal static class UsageExtensions /// Computes the derived property. /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeDefinition(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + // Walk OwnedRelationship → IFeatureTyping → Type directly. Cannot use + // usageSubject.type because the Usage POCO's IFeature.type explicitly delegates + // to .definition (the redefining property), which calls back into this method + // → infinite recursion + stack overflow. + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.OwnedRelationship.OfType().Select(featureTyping => featureTyping.Type).OfType()]; } /// @@ -84,15 +84,16 @@ internal static List ComputeDefinition(this IUsage usageSubject) /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeDirectedUsage(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.directedFeature.OfType()]; } /// @@ -105,15 +106,14 @@ internal static List ComputeDirectedUsage(this IUsage usageSubject) /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static bool ComputeIsReference(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return !usageSubject?.IsComposite ?? throw new ArgumentNullException(nameof(usageSubject)); } /// @@ -134,15 +134,46 @@ internal static bool ComputeIsReference(this IUsage usageSubject) /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static bool ComputeMayTimeVary(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (usageSubject == null) + { + throw new ArgumentNullException(nameof(usageSubject)); + } + + var owningType = usageSubject.owningType; + + if (owningType == null) + { + return false; + } + + if (!owningType.SpecializesFromLibrary("Occurrences::Occurrence")) + { + return false; + } + + if (usageSubject.IsPortion) + { + return false; + } + + if (usageSubject.SpecializesFromLibrary("Links::SelfLink")) + { + return false; + } + + if (usageSubject.SpecializesFromLibrary("Occurrences::HappensLink")) + { + return false; + } + + return !usageSubject.IsComposite || !usageSubject.SpecializesFromLibrary("Actions::Action"); } /// @@ -155,15 +186,16 @@ internal static bool ComputeMayTimeVary(this IUsage usageSubject) /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedAction(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -176,15 +208,16 @@ internal static List ComputeNestedAction(this IUsage usageSubject) /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedAllocation(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -197,15 +230,16 @@ internal static List ComputeNestedAllocation(this IUsage usage /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedAnalysisCase(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -218,15 +252,16 @@ internal static List ComputeNestedAnalysisCase(this IUsage u /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedAttribute(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -239,15 +274,16 @@ internal static List ComputeNestedAttribute(this IUsage usageSu /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedCalculation(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -260,15 +296,16 @@ internal static List ComputeNestedCalculation(this IUsage usa /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedCase(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -281,15 +318,16 @@ internal static List ComputeNestedCase(this IUsage usageSubject) /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedConcern(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -302,15 +340,16 @@ internal static List ComputeNestedConcern(this IUsage usageSubjec /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedConnection(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -323,15 +362,16 @@ internal static List ComputeNestedConnection(this IUsage usag /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedConstraint(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -344,15 +384,16 @@ internal static List ComputeNestedConstraint(this IUsage usage /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedEnumeration(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -365,15 +406,16 @@ internal static List ComputeNestedEnumeration(this IUsage usa /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedFlow(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -386,15 +428,16 @@ internal static List ComputeNestedFlow(this IUsage usageSubject) /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedInterface(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -407,15 +450,16 @@ internal static List ComputeNestedInterface(this IUsage usageSu /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedItem(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -428,15 +472,16 @@ internal static List ComputeNestedItem(this IUsage usageSubject) /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedMetadata(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -449,15 +494,16 @@ internal static List ComputeNestedMetadata(this IUsage usageSubj /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedOccurrence(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -470,15 +516,16 @@ internal static List ComputeNestedOccurrence(this IUsage usage /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedPart(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -491,15 +538,16 @@ internal static List ComputeNestedPart(this IUsage usageSubject) /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedPort(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -512,15 +560,16 @@ internal static List ComputeNestedPort(this IUsage usageSubject) /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedReference(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -533,15 +582,16 @@ internal static List ComputeNestedReference(this IUsage usageSu /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedRendering(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -554,15 +604,16 @@ internal static List ComputeNestedRendering(this IUsage usageSu /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedRequirement(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -575,15 +626,16 @@ internal static List ComputeNestedRequirement(this IUsage usa /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedState(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -596,15 +648,16 @@ internal static List ComputeNestedState(this IUsage usageSubject) /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedTransition(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -617,15 +670,16 @@ internal static List ComputeNestedTransition(this IUsage usage /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedUsage(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.ownedFeature.OfType()]; } /// @@ -638,15 +692,16 @@ internal static List ComputeNestedUsage(this IUsage usageSubject) /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedUseCase(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -659,15 +714,16 @@ internal static List ComputeNestedUseCase(this IUsage usageSubjec /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedVerificationCase(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -680,15 +736,16 @@ internal static List ComputeNestedVerificationCase(this /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedView(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// @@ -701,45 +758,48 @@ internal static List ComputeNestedView(this IUsage usageSubject) /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeNestedViewpoint(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.nestedUsage.OfType()]; } /// /// Computes the derived property. /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IDefinition ComputeOwningDefinition(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : usageSubject.owningType as IDefinition; } /// /// Computes the derived property. /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IUsage ComputeOwningUsage(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : usageSubject.owningType as IUsage; } /// @@ -752,15 +812,16 @@ internal static IUsage ComputeOwningUsage(this IUsage usageSubject) /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeUsage(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.feature.OfType()]; } /// @@ -773,15 +834,21 @@ internal static List ComputeUsage(this IUsage usageSubject) /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeVariant(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : + [ + ..usageSubject.variantMembership + .Select(membership => membership.ownedVariantUsage) + .Where(usage => usage != null) + ]; } /// @@ -794,15 +861,16 @@ internal static List ComputeVariant(this IUsage usageSubject) /// /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeVariantMembership(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : [..usageSubject.ownedMembership.OfType()]; } /// @@ -820,15 +888,27 @@ internal static List ComputeVariantMembership(this IUsage us /// /// /// - /// The subject + /// The subject /// /// /// The expected /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IFeature ComputeRedefinedNamingFeatureOperation(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (usageSubject == null) + { + throw new ArgumentNullException(nameof(usageSubject)); + } + + if (usageSubject.owningMembership is not IVariantMembership) + { + // OCL: self.oclAsType(Feature).namingFeature() — call the Feature-level + // definition directly via the static extension to bypass the Usage-level + // virtual override (which would recurse back into this method). + return usageSubject.ComputeNamingFeatureOperation(); + } + + return usageSubject.ownedReferenceSubsetting?.ReferencedFeature; } /// @@ -844,15 +924,16 @@ internal static IFeature ComputeRedefinedNamingFeatureOperation(this IUsage usag /// /// /// - /// The subject + /// The subject /// /// /// The expected /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IFeature ComputeReferencedFeatureTargetOperation(this IUsage usageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return usageSubject == null + ? throw new ArgumentNullException(nameof(usageSubject)) + : usageSubject.ownedReferenceSubsetting?.ReferencedFeature?.featureTarget; } } }