From 66cb19948f713626529d55afedc47f5f49c9540a Mon Sep 17 00:00:00 2001 From: atheate Date: Mon, 18 May 2026 14:34:35 +0200 Subject: [PATCH] Fix #244 --- .../AcceptActionUsageExtensionsTestFixture.cs | 90 +++++++- .../ActionUsageExtensionsTestFixture.cs | 204 +++++++++++++++++- SysML2.NET/Extend/ActionUsageExtensions.cs | 102 ++++----- 3 files changed, 326 insertions(+), 70 deletions(-) diff --git a/SysML2.NET.Tests/Extend/AcceptActionUsageExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/AcceptActionUsageExtensionsTestFixture.cs index 520487f5..73e4dc11 100644 --- a/SysML2.NET.Tests/Extend/AcceptActionUsageExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/AcceptActionUsageExtensionsTestFixture.cs @@ -24,8 +24,13 @@ namespace SysML2.NET.Tests.Extend 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.Kernel.Expressions; + using SysML2.NET.Core.POCO.Kernel.FeatureValues; using SysML2.NET.Core.POCO.Systems.Actions; + using SysML2.NET.Core.POCO.Systems.DefinitionAndUsage; using SysML2.NET.Core.POCO.Systems.States; using SysML2.NET.Extensions; @@ -37,10 +42,40 @@ public void VerifyComputePayloadArgument() { Assert.That(() => ((IAcceptActionUsage)null).ComputePayloadArgument(), Throws.TypeOf()); - var accept = new AcceptActionUsage(); + // Branch 1 — bare AcceptActionUsage with no input parameters: argument(1) is null. + var emptyAccept = new AcceptActionUsage(); + + Assert.That(emptyAccept.ComputePayloadArgument(), Is.Null); + + // Branch 2 — one input parameter, no FeatureValue membership: argument(1) is null. + var bareParameterAccept = new AcceptActionUsage(); + var bareParameter = new ReferenceUsage { Direction = FeatureDirectionKind.In }; + bareParameterAccept.AssignOwnership(new FeatureMembership(), bareParameter); + + Assert.That(bareParameterAccept.ComputePayloadArgument(), Is.Null); + + // Branch 3 — one input parameter owning a FeatureValue whose value is a LiteralInteger: argument(1) returns that expression. + var populatedAccept = new AcceptActionUsage(); + var populatedParameter = new ReferenceUsage { Direction = FeatureDirectionKind.In }; + populatedAccept.AssignOwnership(new FeatureMembership(), populatedParameter); + + var payloadExpression = new LiteralInteger(); + populatedParameter.AssignOwnership(new FeatureValue(), payloadExpression); - // For Later: populated case depends on ActionUsageExtensions.ComputeArgumentOperation at SysML2.NET/Extend/ActionUsageExtensions.cs:157, which is still a stub. - Assert.That(() => accept.ComputePayloadArgument(), Throws.TypeOf()); + Assert.That(populatedAccept.ComputePayloadArgument(), Is.SameAs(payloadExpression)); + + // Branch 4 — two input parameters, only the second has a FeatureValue: argument(1) must still be null. + var twoParameterAccept = new AcceptActionUsage(); + var firstParameter = new ReferenceUsage { Direction = FeatureDirectionKind.In }; + twoParameterAccept.AssignOwnership(new FeatureMembership(), firstParameter); + + var secondParameter = new ReferenceUsage { Direction = FeatureDirectionKind.In }; + twoParameterAccept.AssignOwnership(new FeatureMembership(), secondParameter); + + var secondExpression = new LiteralInteger(); + secondParameter.AssignOwnership(new FeatureValue(), secondExpression); + + Assert.That(twoParameterAccept.ComputePayloadArgument(), Is.Null); } [Test] @@ -59,10 +94,53 @@ public void VerifyComputeReceiverArgument() { Assert.That(() => ((IAcceptActionUsage)null).ComputeReceiverArgument(), Throws.TypeOf()); - var accept = new AcceptActionUsage(); + // Branch 1 — bare AcceptActionUsage with no input parameters: argument(2) is null. + var emptyAccept = new AcceptActionUsage(); + + Assert.That(emptyAccept.ComputeReceiverArgument(), Is.Null); + + // Branch 2 — one input parameter only: argument(2) is out-of-range and returns null. + var singleParameterAccept = new AcceptActionUsage(); + var loneParameter = new ReferenceUsage { Direction = FeatureDirectionKind.In }; + singleParameterAccept.AssignOwnership(new FeatureMembership(), loneParameter); + + Assert.That(singleParameterAccept.ComputeReceiverArgument(), Is.Null); + + // Branch 3 — two input parameters, neither has a FeatureValue: argument(2) is null. + var bareTwoParameterAccept = new AcceptActionUsage(); + var bareFirstParameter = new ReferenceUsage { Direction = FeatureDirectionKind.In }; + bareTwoParameterAccept.AssignOwnership(new FeatureMembership(), bareFirstParameter); + + var bareSecondParameter = new ReferenceUsage { Direction = FeatureDirectionKind.In }; + bareTwoParameterAccept.AssignOwnership(new FeatureMembership(), bareSecondParameter); + + Assert.That(bareTwoParameterAccept.ComputeReceiverArgument(), Is.Null); + + // Branch 4 — two input parameters, only the SECOND has a FeatureValue with a non-null IExpression: argument(2) returns that expression. + var populatedAccept = new AcceptActionUsage(); + var populatedFirstParameter = new ReferenceUsage { Direction = FeatureDirectionKind.In }; + populatedAccept.AssignOwnership(new FeatureMembership(), populatedFirstParameter); + + var populatedSecondParameter = new ReferenceUsage { Direction = FeatureDirectionKind.In }; + populatedAccept.AssignOwnership(new FeatureMembership(), populatedSecondParameter); + + var receiverExpression = new LiteralInteger(); + populatedSecondParameter.AssignOwnership(new FeatureValue(), receiverExpression); + + Assert.That(populatedAccept.ComputeReceiverArgument(), Is.SameAs(receiverExpression)); + + // Branch 5 — two input parameters, only the FIRST has a FeatureValue: argument(2) must still be null. + var firstOnlyAccept = new AcceptActionUsage(); + var firstOnlyFirstParameter = new ReferenceUsage { Direction = FeatureDirectionKind.In }; + firstOnlyAccept.AssignOwnership(new FeatureMembership(), firstOnlyFirstParameter); + + var firstOnlySecondParameter = new ReferenceUsage { Direction = FeatureDirectionKind.In }; + firstOnlyAccept.AssignOwnership(new FeatureMembership(), firstOnlySecondParameter); + + var firstExpression = new LiteralInteger(); + firstOnlyFirstParameter.AssignOwnership(new FeatureValue(), firstExpression); - // For Later: populated case depends on ActionUsageExtensions.ComputeArgumentOperation at SysML2.NET/Extend/ActionUsageExtensions.cs:157, which is still a stub. - Assert.That(() => accept.ComputeReceiverArgument(), Throws.TypeOf()); + Assert.That(firstOnlyAccept.ComputeReceiverArgument(), Is.Null); } [Test] diff --git a/SysML2.NET.Tests/Extend/ActionUsageExtensionsTestFixture.cs b/SysML2.NET.Tests/Extend/ActionUsageExtensionsTestFixture.cs index 5b9c3344..6ad05967 100644 --- a/SysML2.NET.Tests/Extend/ActionUsageExtensionsTestFixture.cs +++ b/SysML2.NET.Tests/Extend/ActionUsageExtensionsTestFixture.cs @@ -1,38 +1,224 @@ // ------------------------------------------------------------------------------------------------- // -// +// // 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.Classifiers; + 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.Systems.Actions; + using SysML2.NET.Core.POCO.Systems.DefinitionAndUsage; + using SysML2.NET.Core.POCO.Systems.Parts; + using SysML2.NET.Core.POCO.Systems.States; + using SysML2.NET.Core.Systems.States; + using SysML2.NET.Extensions; [TestFixture] public class ActionUsageExtensionsTestFixture { [Test] - public void ComputeActionDefinition_ThrowsNotSupportedException() + public void VerifyComputeActionDefinition() + { + Assert.That(() => ((IActionUsage)null).ComputeActionDefinition(), Throws.TypeOf()); + + var emptyActionUsage = new ActionUsage(); + + Assert.That(emptyActionUsage.ComputeActionDefinition(), Has.Count.EqualTo(0)); + + var actionUsage = new ActionUsage(); + + var firstActionDefinition = new ActionDefinition(); + actionUsage.AssignOwnership(new FeatureTyping { Type = firstActionDefinition }); + + var secondActionDefinition = new ActionDefinition(); + actionUsage.AssignOwnership(new FeatureTyping { Type = secondActionDefinition }); + + // A FeatureTyping whose Type is a plain Classifier (NOT an IBehavior) must be filtered out by OfType(). + var classifier = new Classifier(); + actionUsage.AssignOwnership(new FeatureTyping { Type = classifier }); + + using (Assert.EnterMultipleScope()) + { + Assert.That(actionUsage.ComputeActionDefinition(), Has.Count.EqualTo(2)); + Assert.That(actionUsage.ComputeActionDefinition(), Does.Contain(firstActionDefinition)); + Assert.That(actionUsage.ComputeActionDefinition(), Does.Contain(secondActionDefinition)); + Assert.That(actionUsage.ComputeActionDefinition(), Does.Not.Contain(classifier)); + } + } + + [Test] + public void VerifyComputeInputParametersOperation() + { + Assert.That(() => ((IActionUsage)null).ComputeInputParametersOperation(), Throws.TypeOf()); + + var emptyActionUsage = new ActionUsage(); + + Assert.That(emptyActionUsage.ComputeInputParametersOperation(), Has.Count.EqualTo(0)); + + var actionUsage = new ActionUsage(); + + var firstParameter = new ReferenceUsage { Direction = FeatureDirectionKind.In }; + var firstMembership = new FeatureMembership(); + actionUsage.AssignOwnership(firstMembership, firstParameter); + + var secondParameter = new ReferenceUsage { Direction = FeatureDirectionKind.In }; + var secondMembership = new FeatureMembership(); + actionUsage.AssignOwnership(secondMembership, secondParameter); + + // For later: inherited-vs-owned branch requires a Specialization wiring; covered indirectly. + using (Assert.EnterMultipleScope()) + { + Assert.That(actionUsage.ComputeInputParametersOperation(), Has.Count.EqualTo(2)); + Assert.That(actionUsage.ComputeInputParametersOperation(), Does.Contain(firstParameter)); + Assert.That(actionUsage.ComputeInputParametersOperation(), Does.Contain(secondParameter)); + } + } + + [Test] + public void VerifyComputeInputParameterOperation() + { + Assert.That(() => ((IActionUsage)null).ComputeInputParameterOperation(1), Throws.TypeOf()); + + var emptyActionUsage = new ActionUsage(); + + Assert.That(emptyActionUsage.ComputeInputParameterOperation(1), Is.Null); + + var actionUsage = new ActionUsage(); + + var firstParameter = new ReferenceUsage { Direction = FeatureDirectionKind.In }; + var firstMembership = new FeatureMembership(); + actionUsage.AssignOwnership(firstMembership, firstParameter); + + var secondParameter = new ReferenceUsage { Direction = FeatureDirectionKind.In }; + var secondMembership = new FeatureMembership(); + actionUsage.AssignOwnership(secondMembership, secondParameter); + + using (Assert.EnterMultipleScope()) + { + Assert.That(actionUsage.ComputeInputParameterOperation(0), Is.Null); + Assert.That(actionUsage.ComputeInputParameterOperation(-1), Is.Null); + Assert.That(actionUsage.ComputeInputParameterOperation(1), Is.SameAs(firstParameter)); + Assert.That(actionUsage.ComputeInputParameterOperation(2), Is.SameAs(secondParameter)); + Assert.That(actionUsage.ComputeInputParameterOperation(3), Is.Null); + } + + var singleParameterActionUsage = new ActionUsage(); + var lonelyParameter = new ReferenceUsage { Direction = FeatureDirectionKind.In }; + var lonelyMembership = new FeatureMembership(); + singleParameterActionUsage.AssignOwnership(lonelyMembership, lonelyParameter); + + using (Assert.EnterMultipleScope()) + { + Assert.That(singleParameterActionUsage.ComputeInputParameterOperation(1), Is.SameAs(lonelyParameter)); + Assert.That(singleParameterActionUsage.ComputeInputParameterOperation(2), Is.Null); + } + } + + [Test] + public void VerifyComputeArgumentOperation() { - Assert.That(() => ((IActionUsage)null).ComputeActionDefinition(), Throws.TypeOf()); + Assert.That(() => ((IActionUsage)null).ComputeArgumentOperation(1), Throws.TypeOf()); + + var emptyActionUsage = new ActionUsage(); + + Assert.That(emptyActionUsage.ComputeArgumentOperation(1), Is.Null); + + // ActionUsage with one input parameter that has no FeatureValue membership. + var bareParameterActionUsage = new ActionUsage(); + var bareParameter = new ReferenceUsage { Direction = FeatureDirectionKind.In }; + var bareMembership = new FeatureMembership(); + bareParameterActionUsage.AssignOwnership(bareMembership, bareParameter); + + using (Assert.EnterMultipleScope()) + { + Assert.That(bareParameterActionUsage.ComputeArgumentOperation(0), Is.Null); + Assert.That(bareParameterActionUsage.ComputeArgumentOperation(-1), Is.Null); + Assert.That(bareParameterActionUsage.ComputeArgumentOperation(1), Is.Null); + } + + // ActionUsage with one input parameter owning a FeatureValue with a non-null IExpression value. + var actionUsage = new ActionUsage(); + var inputParameter = new ReferenceUsage { Direction = FeatureDirectionKind.In }; + var inputMembership = new FeatureMembership(); + actionUsage.AssignOwnership(inputMembership, inputParameter); + + var expression = new LiteralInteger(); + var featureValue = new FeatureValue(); + inputParameter.AssignOwnership(featureValue, expression); + + using (Assert.EnterMultipleScope()) + { + Assert.That(actionUsage.ComputeArgumentOperation(1), Is.SameAs(expression)); + Assert.That(actionUsage.ComputeArgumentOperation(2), Is.Null); + } + } + + [Test] + public void VerifyComputeIsSubactionUsageOperation() + { + Assert.That(() => ((IActionUsage)null).ComputeIsSubactionUsageOperation(), Throws.TypeOf()); + + // Branch 1: IsComposite = false -> false. + var nonCompositeActionUsage = new ActionUsage { IsComposite = false }; + + Assert.That(nonCompositeActionUsage.ComputeIsSubactionUsageOperation(), Is.False); + + // Branch 2: composite, no owner -> false (owningType is null). + var orphanCompositeActionUsage = new ActionUsage { IsComposite = true }; + + Assert.That(orphanCompositeActionUsage.ComputeIsSubactionUsageOperation(), Is.False); + + // Branch 3: composite, owningType is a PartDefinition (not ActionDef/ActionUsage) -> false. + var partDefinitionOwner = new PartDefinition(); + var partOwnedActionUsage = new ActionUsage { IsComposite = true }; + partDefinitionOwner.AssignOwnership(new FeatureMembership(), partOwnedActionUsage); + + Assert.That(partOwnedActionUsage.ComputeIsSubactionUsageOperation(), Is.False); + + // Branch 4: composite, owned by ActionDefinition via plain FeatureMembership -> true. + var actionDefinitionOwner = new ActionDefinition(); + var actionDefOwnedActionUsage = new ActionUsage { IsComposite = true }; + actionDefinitionOwner.AssignOwnership(new FeatureMembership(), actionDefOwnedActionUsage); + + Assert.That(actionDefOwnedActionUsage.ComputeIsSubactionUsageOperation(), Is.True); + + // Branch 5: composite, owned by ActionUsage parent via StateSubactionMembership { Kind = Do } -> true. + var actionUsageParent = new ActionUsage(); + var doSubaction = new ActionUsage { IsComposite = true }; + actionUsageParent.AssignOwnership(new StateSubactionMembership { Kind = StateSubactionKind.Do }, doSubaction); + + Assert.That(doSubaction.ComputeIsSubactionUsageOperation(), Is.True); + + // Branch 6: composite, owned by ActionDefinition via StateSubactionMembership { Kind = Entry } -> false. + var entryOwner = new ActionDefinition(); + var entrySubaction = new ActionUsage { IsComposite = true }; + entryOwner.AssignOwnership(new StateSubactionMembership { Kind = StateSubactionKind.Entry }, entrySubaction); + + Assert.That(entrySubaction.ComputeIsSubactionUsageOperation(), Is.False); } } } diff --git a/SysML2.NET/Extend/ActionUsageExtensions.cs b/SysML2.NET/Extend/ActionUsageExtensions.cs index 83f300be..618be645 100644 --- a/SysML2.NET/Extend/ActionUsageExtensions.cs +++ b/SysML2.NET/Extend/ActionUsageExtensions.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,44 +22,18 @@ namespace SysML2.NET.Core.POCO.Systems.Actions { using System; using System.Collections.Generic; + using System.Linq; - using SysML2.NET.Core.Core.Types; - using SysML2.NET.Core.Root.Namespaces; - using SysML2.NET.Core.Systems.Occurrences; - using SysML2.NET.Core.POCO.Core.Classifiers; using SysML2.NET.Core.POCO.Core.Features; - using SysML2.NET.Core.POCO.Core.Types; using SysML2.NET.Core.POCO.Kernel.Behaviors; - using SysML2.NET.Core.POCO.Kernel.Classes; + using SysML2.NET.Core.POCO.Kernel.FeatureValues; using SysML2.NET.Core.POCO.Kernel.Functions; - 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.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.Core.Systems.States; /// - /// The class provides extensions methods for - /// the interface + /// The class provides extensions methods for + /// the interface /// internal static class ActionUsageExtensions { @@ -67,15 +41,16 @@ internal static class ActionUsageExtensions /// Computes the derived property. /// /// - /// The subject + /// The subject /// /// /// the computed result /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeActionDefinition(this IActionUsage actionUsageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return actionUsageSubject == null + ? throw new ArgumentNullException(nameof(actionUsageSubject)) + : [.. actionUsageSubject.OwnedRelationship.OfType().Select(featureTyping => featureTyping.Type).OfType()]; } /// @@ -88,15 +63,16 @@ internal static List ComputeActionDefinition(this IActionUsage action /// /// /// - /// The subject + /// The subject /// /// /// The expected collection of /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static List ComputeInputParametersOperation(this IActionUsage actionUsageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return actionUsageSubject == null + ? throw new ArgumentNullException(nameof(actionUsageSubject)) + : [.. actionUsageSubject.input.Where(inputFeature => inputFeature.owner == actionUsageSubject)]; } /// @@ -112,7 +88,7 @@ internal static List ComputeInputParametersOperation(this IActionUsage /// /// /// - /// The subject + /// The subject /// /// /// No documentation provided @@ -120,10 +96,18 @@ internal static List ComputeInputParametersOperation(this IActionUsage /// /// The expected /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IFeature ComputeInputParameterOperation(this IActionUsage actionUsageSubject, int i) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (actionUsageSubject == null) + { + throw new ArgumentNullException(nameof(actionUsageSubject)); + } + + var inputParameters = actionUsageSubject.InputParameters(); + + return i <= 0 || inputParameters.Count < i + ? null + : inputParameters[i - 1]; } /// @@ -145,7 +129,7 @@ internal static IFeature ComputeInputParameterOperation(this IActionUsage action /// /// /// - /// The subject + /// The subject /// /// /// No documentation provided @@ -153,10 +137,11 @@ internal static IFeature ComputeInputParameterOperation(this IActionUsage action /// /// The expected /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static IExpression ComputeArgumentOperation(this IActionUsage actionUsageSubject, int i) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + return actionUsageSubject == null + ? throw new ArgumentNullException(nameof(actionUsageSubject)) + : actionUsageSubject.InputParameter(i)?.ownedMembership.OfType().FirstOrDefault()?.value; } /// @@ -176,15 +161,22 @@ internal static IExpression ComputeArgumentOperation(this IActionUsage actionUsa /// /// /// - /// The subject + /// The subject /// /// /// The expected /// - [System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] internal static bool ComputeIsSubactionUsageOperation(this IActionUsage actionUsageSubject) { - throw new NotSupportedException("Create a GitHub issue when this method is required"); + if (actionUsageSubject == null) + { + throw new ArgumentNullException(nameof(actionUsageSubject)); + } + + return actionUsageSubject.IsComposite + && actionUsageSubject.owningType is IActionDefinition or IActionUsage + && (actionUsageSubject.owningFeatureMembership is not IStateSubactionMembership ssm + || ssm.Kind == StateSubactionKind.Do); } } }