diff --git a/Core/Stateflows.Common/Extensions/ObjectExtensions.cs b/Core/Stateflows.Common/Extensions/ObjectExtensions.cs index 31c833b8..b6dda4fc 100644 --- a/Core/Stateflows.Common/Extensions/ObjectExtensions.cs +++ b/Core/Stateflows.Common/Extensions/ObjectExtensions.cs @@ -1,6 +1,6 @@ using System; -using System.Collections.Generic; using System.Linq; +using System.Collections.Generic; using Stateflows.Activities; namespace Stateflows.Common diff --git a/Core/Stateflows/Activities/Context/Classes/RootContext.cs b/Core/Stateflows/Activities/Context/Classes/RootContext.cs index a93e8bf9..c3360b39 100644 --- a/Core/Stateflows/Activities/Context/Classes/RootContext.cs +++ b/Core/Stateflows/Activities/Context/Classes/RootContext.cs @@ -92,13 +92,13 @@ public Dictionary GetStreams(Guid threadId) } } - internal IEnumerable GetActivatedStreams(Node node, Guid threadId) + internal IEnumerable GetNodeStreams(Node node, Guid threadId, bool activatedOnly) { lock (Streams) { return node.IncomingEdges .Select(edge => GetStream(edge.Identifier, threadId)) - .Where(stream => stream.IsActivated) + .Where(stream => stream.IsActivated || !activatedOnly) .ToArray(); } } diff --git a/Core/Stateflows/Activities/Context/Classes/SourceNodeContext.cs b/Core/Stateflows/Activities/Context/Classes/SourceNodeContext.cs index 7a3f6e35..81467196 100644 --- a/Core/Stateflows/Activities/Context/Classes/SourceNodeContext.cs +++ b/Core/Stateflows/Activities/Context/Classes/SourceNodeContext.cs @@ -1,10 +1,10 @@ using System; using System.Linq; using System.Collections.Generic; -using Stateflows.Activities.Models; -using Stateflows.Activities.Context.Interfaces; using Stateflows.Utils; using Stateflows.Activities.Engine; +using Stateflows.Activities.Models; +using Stateflows.Activities.Context.Interfaces; namespace Stateflows.Activities.Context.Classes { @@ -20,7 +20,7 @@ public SourceNodeContext(Node node, RootContext context, NodeScope nodeScope) public IEnumerable GetTokensOfType() => Context - .GetActivatedStreams(Node, ThreadId) + .GetNodeStreams(Node, ThreadId, true) .SelectMany(stream => stream.Tokens) .OfType>() .ToTokens() diff --git a/Core/Stateflows/Activities/Engine/Executor.cs b/Core/Stateflows/Activities/Engine/Executor.cs index 9c12681b..ec3fc043 100644 --- a/Core/Stateflows/Activities/Engine/Executor.cs +++ b/Core/Stateflows/Activities/Engine/Executor.cs @@ -17,6 +17,7 @@ using Stateflows.Activities.Streams; using Stateflows.Activities.Registration; using Stateflows.Activities.Context.Classes; +using Stateflows.Activities.Enums; namespace Stateflows.Activities.Engine { @@ -641,7 +642,7 @@ public async Task DoInitializeActivityAsync HandleExceptionAsync(Node node, Exception exception, BaseContext context) + public async Task HandleExceptionAsync(Node node, Exception exception, ActionContext context) { Node handler = null; var currentNode = node; @@ -689,14 +690,23 @@ public async Task HandleExceptionAsync(Node node, Exception exception, Bas await handler.Action.WhenAll(exceptionContext); - DoHandleOutput(exceptionContext); + if (node.ExceptionHandlers.Contains(handler)) + { + context.OutputTokens.AddRange(exceptionContext.OutputTokens); + } + else + { + DoHandleOutput(exceptionContext); + } ReportExceptionHandled(node, exceptionName, exceptionContext.OutputTokens.Where(t => t is TokenHolder).ToArray(), Context); - return true; + return node.ExceptionHandlers.Contains(handler) + ? ExceptionHandlingResult.HandledDirectly + : ExceptionHandlingResult.HandledIndirectly; } - return false; + return ExceptionHandlingResult.NotHandled; } public async Task DoHandleNodeAsync(Node node, Edge upstreamEdge, NodeScope nodeScope, IEnumerable input = null, IEnumerable selectionTokens = null) @@ -713,7 +723,7 @@ public async Task DoHandleNodeAsync(Node node, Edge upstreamEdge, NodeScope node lock (GetLock(node)) { var streams = !Context.NodesToExecute.Contains(node) - ? Context.GetActivatedStreams(node, nodeScope.ThreadId) + ? Context.GetNodeStreams(node, nodeScope.ThreadId, node.Type != NodeType.Output) : Array.Empty(); activated = @@ -832,13 +842,14 @@ public async Task DoHandleNodeAsync(Node node, Edge upstreamEdge, NodeScope node ReportNodeExecuted(node, outputTokens.Where(t => t is TokenHolder).ToArray(), Context); - if (outputTokens.Any(t => t is TokenHolder)) + //if (outputTokens.Any(t => t is TokenHolder)) { var tokenNames = outputTokens.Select(token => token.Name).Distinct().ToArray(); var nodes = ( await Task.WhenAll( node.Edges + .Where(edge => edge.Target.Type == NodeType.Output || outputTokens.Any(t => t is TokenHolder)) .Where(edge => tokenNames.Contains(edge.TokenType.GetTokenName()) || edge.Weight == 0) .OrderBy(edge => edge.IsElse) .Select(async edge => ( @@ -961,7 +972,7 @@ private static void ReportMissingFlows(IEnumerable flows) } } - private void DoHandleOutput(ActionContext context) + internal void DoHandleOutput(ActionContext context) => context.Context.GetOutputTokens(context.Node.Identifier, context.NodeScope.ThreadId).AddRange(context.OutputTokens); private async Task DoHandleEdgeAsync(Edge edge, ActionContext context) diff --git a/Core/Stateflows/Activities/Enums/ExceptionHandlingResult.cs b/Core/Stateflows/Activities/Enums/ExceptionHandlingResult.cs new file mode 100644 index 00000000..ae1e2315 --- /dev/null +++ b/Core/Stateflows/Activities/Enums/ExceptionHandlingResult.cs @@ -0,0 +1,9 @@ +namespace Stateflows.Activities.Enums +{ + internal enum ExceptionHandlingResult + { + NotHandled, + HandledDirectly, + HandledIndirectly + } +} diff --git a/Core/Stateflows/Activities/Models/Node.cs b/Core/Stateflows/Activities/Models/Node.cs index 14c62993..79ebd479 100644 --- a/Core/Stateflows/Activities/Models/Node.cs +++ b/Core/Stateflows/Activities/Models/Node.cs @@ -30,10 +30,7 @@ internal class Node : Element public int ChunkSize { get; set; } public bool Anchored { get; set; } = true; - public Logic Action { get; } = new Logic() - { - Name = Constants.Action - }; + public Logic Action { get; } = new Logic(Constants.Action); public List Edges { get; set; } = new List(); public List IncomingEdges { get; set; } = new List(); @@ -94,27 +91,19 @@ public void ScanForDeclaredTypes(Type nodeType) .ToList(); } - private Logic initialize = null; - public Logic Initialize - => initialize ??= new Logic() - { - Name = Constants.Initialize - }; + public Logic Initialize { get; } = + new Logic(Constants.Initialize); - private Logic finalize = null; - public Logic Finalize - => finalize ??= new Logic() - { - Name = Constants.Finalize - }; + public Logic Finalize { get; } = + new Logic(Constants.Finalize); - private IEnumerable initialNodes = null; + private IEnumerable initialNodes; public IEnumerable InitialNodes => initialNodes ??= Nodes.Values .Where(n => n.Type == NodeType.Initial); - private Node inputNode = null; - private bool inputNodeSet = false; + private Node inputNode; + private bool inputNodeSet; public Node InputNode { get @@ -129,8 +118,8 @@ public Node InputNode } } - private Node outputNode = null; - private bool outputNodeSet = false; + private Node outputNode; + private bool outputNodeSet; public Node OutputNode { get @@ -145,17 +134,17 @@ public Node OutputNode } } - private IEnumerable acceptEventActionNodes = null; + private IEnumerable acceptEventActionNodes; public IEnumerable AcceptEventActionNodes => acceptEventActionNodes ??= Nodes.Values .Where(n => n.Type == NodeType.AcceptEventAction || n.Type == NodeType.TimeEventAction); - private IEnumerable danglingTimeEventActionNodes = null; + private IEnumerable danglingTimeEventActionNodes; public IEnumerable DanglingTimeEventActionNodes => danglingTimeEventActionNodes ??= AcceptEventActionNodes .Where(n => !n.IncomingEdges.Any() && n.ActualEventTypes.Any(type => type.IsSubclassOf(typeof(TimeEvent)))); - private IEnumerable exceptionHandlers = null; + private IEnumerable exceptionHandlers; public IEnumerable ExceptionHandlers => exceptionHandlers ??= Edges .Select(e => e.Target) diff --git a/Core/Stateflows/Activities/Registration/Builders/ActivityBuilder.cs b/Core/Stateflows/Activities/Registration/Builders/ActivityBuilder.cs index 15359dcd..23062783 100644 --- a/Core/Stateflows/Activities/Registration/Builders/ActivityBuilder.cs +++ b/Core/Stateflows/Activities/Registration/Builders/ActivityBuilder.cs @@ -37,10 +37,7 @@ public IActivityBuilder AddInitializer(Type initializerType, string initializerN { if (!Result.Initializers.TryGetValue(initializerName, out var initializer)) { - initializer = new Logic() - { - Name = Constants.Initialize - }; + initializer = new Logic(Constants.Initialize); Result.Initializers.Add(initializerName, initializer); Result.InitializerTypes.Add(initializerType); @@ -53,10 +50,7 @@ public IActivityBuilder AddInitializer(Type initializerType, string initializerN public IActivityBuilder AddDefaultInitializer(Func> actionAsync) { - Result.DefaultInitializer = new Logic() - { - Name = Constants.Initialize - }; + Result.DefaultInitializer = new Logic(Constants.Initialize); Result.DefaultInitializer.Actions.Add(c => { @@ -164,16 +158,6 @@ public IActivityBuilder AddExceptionHandler() return this; } - //public IActivityBuilder AddExceptionHandler(ExceptionHandlerDelegateAsync exceptionHandler) - // where TException : Exception - //{ - // AddExceptionHandler(serviceProvider - // => new AnonymousExceptionHandler(new NodeScope(serviceProvider, Node, Guid.NewGuid()), Node, exceptionHandler) - // ); - - // return this; - //} - public IActivityBuilder AddExceptionHandler(ActivityExceptionHandlerFactory exceptionHandlerFactory) { Result.ExceptionHandlerFactories.Add(exceptionHandlerFactory); diff --git a/Core/Stateflows/Activities/Registration/Builders/BaseActivityBuilder.cs b/Core/Stateflows/Activities/Registration/Builders/BaseActivityBuilder.cs index 3421c210..b5e2699b 100644 --- a/Core/Stateflows/Activities/Registration/Builders/BaseActivityBuilder.cs +++ b/Core/Stateflows/Activities/Registration/Builders/BaseActivityBuilder.cs @@ -11,6 +11,8 @@ using Stateflows.Activities.Context.Interfaces; using Stateflows.Activities.Registration.Builders; using Stateflows.Activities.Registration.Interfaces; +using System.Threading; +using Stateflows.Activities.Enums; namespace Stateflows.Activities.Registration { @@ -119,17 +121,14 @@ public BaseActivityBuilder AddNode(NodeType type, string nodeName, ActionDelegat node.Action.Actions.Add(async c => { + var context = c as ActionContext; + var faulty = false; try { var inspector = c.Activity.GetExecutor().Inspector; - await inspector.BeforeNodeExecuteAsync(c as ActionContext); + await inspector.BeforeNodeExecuteAsync(context); await actionAsync(c); - await inspector.AfterNodeExecuteAsync(c as ActionContext); - - if (!(c as BaseContext).Context.Executor.StructuralTypes.Contains(node.Type)) - { - c.Output(new ControlToken()); - } + await inspector.AfterNodeExecuteAsync(context); } catch (Exception e) { @@ -139,14 +138,27 @@ public BaseActivityBuilder AddNode(NodeType type, string nodeName, ActionDelegat } else { - if (!await (c as BaseContext).Context.Executor.HandleExceptionAsync(node, e, c as BaseContext)) + var executor = context.Context.Executor; + var result = await executor.HandleExceptionAsync(node, e, context); + if (result == ExceptionHandlingResult.NotHandled) { + faulty = true; throw; } + else + { + faulty = result == ExceptionHandlingResult.HandledIndirectly; + } } } - } - ); + finally + { + if (!faulty && !context.Context.Executor.StructuralTypes.Contains(node.Type)) + { + c.Output(new ControlToken()); + } + } + }); Node.Nodes.Add(node.Identifier, node); Result.AllNodes.Add(node.Identifier, node); diff --git a/Core/Stateflows/Activities/Registration/Builders/FlowBuilder.cs b/Core/Stateflows/Activities/Registration/Builders/FlowBuilder.cs index 1000d159..e0113ef7 100644 --- a/Core/Stateflows/Activities/Registration/Builders/FlowBuilder.cs +++ b/Core/Stateflows/Activities/Registration/Builders/FlowBuilder.cs @@ -35,10 +35,7 @@ public FlowBuilder(Edge edge, IServiceCollection services) public IObjectFlowBuilder AddGuard(GuardDelegateAsync guardAsync) { - var logic = new Logic() - { - Name = Constants.Guard - }; + var logic = new Logic(Constants.Guard); logic.Actions.Add(async context => { @@ -77,10 +74,7 @@ await guardAsync(new TokenFlowContext(context, token.Payload)) public IObjectFlowBuilder AddTransformation(TransformationDelegateAsync transformationAsync) { - var logic = new Logic() - { - Name = Constants.Guard - }; + var logic = new Logic(Constants.Guard); logic.Actions.Add(async context => { diff --git a/Core/Stateflows/Common/Attributes/BehaviorAttribute.cs b/Core/Stateflows/Common/Attributes/BehaviorAttribute.cs index df3a81f4..f11d0b46 100644 --- a/Core/Stateflows/Common/Attributes/BehaviorAttribute.cs +++ b/Core/Stateflows/Common/Attributes/BehaviorAttribute.cs @@ -9,7 +9,7 @@ public abstract class BehaviorAttribute : Attribute public int Version { get; set; } - public BehaviorAttribute(string name, int version = 1) + protected BehaviorAttribute(string name, int version = 1) { Name = name; Version = version; diff --git a/Core/Stateflows/Common/Engine/StateflowsEngine.cs b/Core/Stateflows/Common/Engine/StateflowsEngine.cs index 5cc5ae2c..bd8e8409 100644 --- a/Core/Stateflows/Common/Engine/StateflowsEngine.cs +++ b/Core/Stateflows/Common/Engine/StateflowsEngine.cs @@ -55,7 +55,7 @@ public Task StartAsync(CancellationToken cancellationToken) { executionTask = Task.Run(() => { - while (!CancellationTokenSource.Token.IsCancellationRequested) + while (!CancellationTokenSource.Token.IsCancellationRequested && !cancellationToken.IsCancellationRequested) { if (!EventQueue.WaitAsync(CancellationTokenSource.Token).GetAwaiter().GetResult()) { diff --git a/Core/Stateflows/Common/Models/Logic.cs b/Core/Stateflows/Common/Models/Logic.cs index e6e2435c..16cca545 100644 --- a/Core/Stateflows/Common/Models/Logic.cs +++ b/Core/Stateflows/Common/Models/Logic.cs @@ -6,8 +6,13 @@ namespace Stateflows.Common.Models internal class Logic where TDelegate : Delegate { + public Logic(string name) + { + Name = name; + } + public List Actions { get; set; } = new List(); - public string Name { get; set; } + public string Name { get; } } } \ No newline at end of file diff --git a/Core/Stateflows/Common/Scheduler/ScheduleExecutor.cs b/Core/Stateflows/Common/Scheduler/ScheduleExecutor.cs index bffd3918..d6815c77 100644 --- a/Core/Stateflows/Common/Scheduler/ScheduleExecutor.cs +++ b/Core/Stateflows/Common/Scheduler/ScheduleExecutor.cs @@ -62,7 +62,7 @@ public async Task ExecuteAsync() { _ = SendAsyncMethod .MakeGenericMethod(timeEvent.GetType()) - .Invoke(behavior, new object[] { timeEvent }); + .Invoke(behavior, new object[] { timeEvent, null }); } } } diff --git a/Core/Stateflows/StateMachines/Context/Classes/BaseContext.cs b/Core/Stateflows/StateMachines/Context/Classes/BaseContext.cs index 8efcb150..13b97647 100644 --- a/Core/Stateflows/StateMachines/Context/Classes/BaseContext.cs +++ b/Core/Stateflows/StateMachines/Context/Classes/BaseContext.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using System.Collections.Generic; +using Microsoft.Extensions.DependencyInjection; using Stateflows.Common; using Stateflows.Common.Context; using Stateflows.Common.Interfaces; @@ -16,6 +17,8 @@ public BaseContext(RootContext context) public object ExecutionTrigger => Context.ExecutionTriggerHolder.BoxedPayload; + public IEnumerable ExecutionSteps => Context.ExecutionSteps; + public StateMachineContext stateMachine; public StateMachineContext StateMachine => stateMachine ??= new StateMachineContext(Context); diff --git a/Core/Stateflows/StateMachines/Context/Classes/ExecutionStep.cs b/Core/Stateflows/StateMachines/Context/Classes/ExecutionStep.cs new file mode 100644 index 00000000..0933f0b3 --- /dev/null +++ b/Core/Stateflows/StateMachines/Context/Classes/ExecutionStep.cs @@ -0,0 +1,18 @@ +namespace Stateflows.StateMachines.Context.Classes +{ + internal class ExecutionStep : IExecutionStep + { + public ExecutionStep(string sourceStateName, string targetStateName, object transitionTrigger) + { + SourceStateName = sourceStateName; + TargetStateName = targetStateName; + TransitionTrigger = transitionTrigger; + } + + public string SourceStateName { get; } + + public string TargetStateName { get; } + + public object TransitionTrigger { get; } + } +} diff --git a/Core/Stateflows/StateMachines/Context/Classes/RootContext.cs b/Core/Stateflows/StateMachines/Context/Classes/RootContext.cs index dfb1cdd0..aafcf459 100644 --- a/Core/Stateflows/StateMachines/Context/Classes/RootContext.cs +++ b/Core/Stateflows/StateMachines/Context/Classes/RootContext.cs @@ -14,19 +14,30 @@ internal class RootContext { public readonly StateMachineId Id; - internal readonly StateflowsContext Context; + private StateflowsContext stateflowsContext; + + internal StateflowsContext Context => stateflowsContext; internal readonly Executor Executor; public RootContext(StateflowsContext context, Executor executor, EventHolder @event) { - Context = context; - Id = Context.Id; + stateflowsContext = context; + Id = stateflowsContext.Id; Executor = executor; SetEvent(@event); } - public Dictionary GlobalValues => Context.GlobalValues; + public void SetContext(StateflowsContext context) + { + stateflowsContext = context; + deferredEvents = null; + embeddedBehaviorStatuses = null; + stateValues = null; + statesStack = null; + } + + public Dictionary GlobalValues => stateflowsContext.GlobalValues; private List deferredEvents = null; public List DeferredEvents @@ -35,10 +46,10 @@ public List DeferredEvents { if (deferredEvents == null) { - if (!Context.Values.TryGetValue(Constants.DeferredEvents, out var deferredEventsObj)) + if (!stateflowsContext.Values.TryGetValue(Constants.DeferredEvents, out var deferredEventsObj)) { deferredEvents = new List(); - Context.Values[Constants.DeferredEvents] = deferredEvents; + stateflowsContext.Values[Constants.DeferredEvents] = deferredEvents; } else { @@ -46,7 +57,7 @@ public List DeferredEvents if (deferredEvents == null) { deferredEvents = new List(); - Context.Values[Constants.DeferredEvents] = deferredEvents; + stateflowsContext.Values[Constants.DeferredEvents] = deferredEvents; } } } @@ -62,10 +73,10 @@ public Dictionary EmbeddedBehaviorStatuses { if (embeddedBehaviorStatuses == null) { - if (!Context.Values.TryGetValue(Constants.EmbeddedBehaviorStatuses, out var stateContextsObj)) + if (!stateflowsContext.Values.TryGetValue(Constants.EmbeddedBehaviorStatuses, out var stateContextsObj)) { embeddedBehaviorStatuses = new Dictionary(); - Context.Values[Constants.EmbeddedBehaviorStatuses] = embeddedBehaviorStatuses; + stateflowsContext.Values[Constants.EmbeddedBehaviorStatuses] = embeddedBehaviorStatuses; } else { @@ -84,10 +95,10 @@ public Dictionary StateValues { if (stateValues == null) { - if (!Context.Values.TryGetValue(Constants.StateValues, out var stateContextsObj)) + if (!stateflowsContext.Values.TryGetValue(Constants.StateValues, out var stateContextsObj)) { stateValues = new Dictionary(); - Context.Values[Constants.StateValues] = stateValues; + stateflowsContext.Values[Constants.StateValues] = stateValues; } else { @@ -125,10 +136,10 @@ public List StatesStack { if (statesStack == null) { - if (!Context.Values.TryGetValue(Constants.StatesStack, out var statesStackObj)) + if (!stateflowsContext.Values.TryGetValue(Constants.StatesStack, out var statesStackObj)) { statesStack = new List(); - Context.Values[Constants.StatesStack] = statesStack; + stateflowsContext.Values[Constants.StatesStack] = statesStack; } else { @@ -162,16 +173,34 @@ public void ClearEvent() ? EventsStack.Last() : null; - public readonly List Exceptions = new List(); + private readonly List executionSteps = new List(); + + public IEnumerable ExecutionSteps => executionSteps; + + public void AddExecutionStep(string sourceStateName, string targetStateName, object transitionTrigger) + => executionSteps.Add(new ExecutionStep(sourceStateName, targetStateName, transitionTrigger)); + + private readonly List exceptions = new List(); + + public IEnumerable Exceptions => exceptions; + + public void AddException(Exception exception) + => exceptions.Add(exception); + + public void RemoveException(Exception exception) + => exceptions.Remove(exception); + + public void ClearExceptions() + => exceptions.Clear(); public EventStatus? ForceStatus { get; set; } = null; - public async Task Send(TEvent @event, IEnumerable headers = null) + public async Task SendAsync(TEvent @event, IEnumerable headers = null) { var locator = Executor.ServiceProvider.GetService(); if (locator != null && locator.TryLocateBehavior(Id, out var behavior)) { - await behavior.SendAsync(@event, headers); + await behavior.SendAsync(@event, headers).ConfigureAwait(false); } } } diff --git a/Core/Stateflows/StateMachines/Context/Classes/StateMachineActionContext.cs b/Core/Stateflows/StateMachines/Context/Classes/StateMachineActionContext.cs index b9b95770..e38ad9e5 100644 --- a/Core/Stateflows/StateMachines/Context/Classes/StateMachineActionContext.cs +++ b/Core/Stateflows/StateMachines/Context/Classes/StateMachineActionContext.cs @@ -3,7 +3,7 @@ namespace Stateflows.StateMachines.Context.Classes { - internal class StateMachineActionContext : BaseContext, IStateMachineActionContext, IStateMachineActionInspectionContext, IRootContext + internal class StateMachineActionContext : BaseContext, IStateMachineActionInspectionContext, IRootContext { IStateMachineContext IStateMachineActionContext.StateMachine => StateMachine; diff --git a/Core/Stateflows/StateMachines/Context/Classes/StateMachineContext.cs b/Core/Stateflows/StateMachines/Context/Classes/StateMachineContext.cs index c23a5ace..bf0f5b9a 100644 --- a/Core/Stateflows/StateMachines/Context/Classes/StateMachineContext.cs +++ b/Core/Stateflows/StateMachines/Context/Classes/StateMachineContext.cs @@ -31,7 +31,7 @@ public StateMachineContext(RootContext context) : base(context) public void Send(TEvent @event, IEnumerable headers = null) - => _ = Context.Send(@event, headers); + => _ = Context.SendAsync(@event, headers); public void Publish(TNotificationEvent notification, IEnumerable headers = null) => _ = Subscriber.PublishAsync(Id, notification, headers); diff --git a/Core/Stateflows/StateMachines/Context/Classes/TransitionContext.cs b/Core/Stateflows/StateMachines/Context/Classes/TransitionContext.cs index 2cdf8da5..a66fcc4e 100644 --- a/Core/Stateflows/StateMachines/Context/Classes/TransitionContext.cs +++ b/Core/Stateflows/StateMachines/Context/Classes/TransitionContext.cs @@ -1,45 +1,46 @@ -using Stateflows.Common; -using Stateflows.StateMachines.Models; -using Stateflows.StateMachines.Context.Interfaces; -using Stateflows.StateMachines.Inspection.Interfaces; - -namespace Stateflows.StateMachines.Context.Classes -{ - internal class TransitionContext : EventContext, - ITransitionInspectionContext, - IEdgeContext, - IRootContext - { - public Edge Edge { get; } - - public TransitionContext(RootContext context, Edge edge) : base(context) - { - Edge = edge; - } - - IStateMachineInspectionContext ITransitionInspectionContext.StateMachine => StateMachine; - - private IStateContext sourceState = null; - public IStateContext SourceState => sourceState ??= new StateContext(Edge.Source, Context); - - private bool targetStateSet = false; - private IStateContext targetState = null; - public IStateContext TargetState - { - get - { - if (!targetStateSet) - { - targetStateSet = true; - - if (!(Edge.Target is null)) - { - targetState = new StateContext(Edge.Target, Context); - } - } - - return targetState; - } - } - } -} +using Stateflows.Common; +using Stateflows.StateMachines.Models; +using Stateflows.StateMachines.Context.Interfaces; +using Stateflows.StateMachines.Inspection.Interfaces; + +namespace Stateflows.StateMachines.Context.Classes +{ + internal class TransitionContext : EventContext, + ITransitionInspectionContext, + IEdgeContext, + IRootContext + + { + public Edge Edge { get; } + + public TransitionContext(RootContext context, Edge edge) : base(context) + { + Edge = edge; + } + + IStateMachineInspectionContext ITransitionInspectionContext.StateMachine => StateMachine; + + private IStateContext sourceState = null; + public IStateContext SourceState => sourceState ??= new StateContext(Edge.Source, Context); + + private bool targetStateSet = false; + private IStateContext targetState = null; + public IStateContext TargetState + { + get + { + if (!targetStateSet) + { + targetStateSet = true; + + if (!(Edge.Target is null)) + { + targetState = new StateContext(Edge.Target, Context); + } + } + + return targetState; + } + } + } +} diff --git a/Core/Stateflows/StateMachines/Context/Interfaces/IStateMachineActionContext.cs b/Core/Stateflows/StateMachines/Context/Interfaces/IStateMachineActionContext.cs index e68de1d6..6aa74af5 100644 --- a/Core/Stateflows/StateMachines/Context/Interfaces/IStateMachineActionContext.cs +++ b/Core/Stateflows/StateMachines/Context/Interfaces/IStateMachineActionContext.cs @@ -2,11 +2,6 @@ namespace Stateflows.StateMachines.Context.Interfaces { - public interface IInitializationContext - { - EventHolder InitializationEvent { get; } - } - public interface IStateMachineActionContext : IBehaviorLocator, IExecutionContext { IStateMachineContext StateMachine { get; } diff --git a/Core/Stateflows/StateMachines/Context/StateMachinesContextHolder.cs b/Core/Stateflows/StateMachines/Context/StateMachinesContextHolder.cs index 854e63cb..75cfbdb3 100644 --- a/Core/Stateflows/StateMachines/Context/StateMachinesContextHolder.cs +++ b/Core/Stateflows/StateMachines/Context/StateMachinesContextHolder.cs @@ -1,5 +1,4 @@ using System.Threading; -using Stateflows.Common; using Stateflows.Common.Context; namespace Stateflows.StateMachines.Context @@ -9,6 +8,7 @@ public static class StateMachinesContextHolder public static readonly AsyncLocal StateMachineContext = new AsyncLocal(); public static readonly AsyncLocal StateContext = new AsyncLocal(); public static readonly AsyncLocal TransitionContext = new AsyncLocal(); - public static AsyncLocal ExecutionContext => CommonContextHolder.ExecutionContext; + public static readonly AsyncLocal ExecutionContext = new AsyncLocal(); + public static AsyncLocal CommonExecutionContext => CommonContextHolder.ExecutionContext; } } diff --git a/Core/Stateflows/StateMachines/Engine/Executor.cs b/Core/Stateflows/StateMachines/Engine/Executor.cs index d9d50def..0ab54e44 100644 --- a/Core/Stateflows/StateMachines/Engine/Executor.cs +++ b/Core/Stateflows/StateMachines/Engine/Executor.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection; -using Stateflows.Utils; using Stateflows.Common; using Stateflows.Common.Context; using Stateflows.Common.Classes; @@ -18,7 +17,6 @@ using Stateflows.StateMachines.Registration; using Stateflows.StateMachines.Context.Classes; using Stateflows.StateMachines.Context.Interfaces; -using System.Runtime.InteropServices; namespace Stateflows.StateMachines.Engine { @@ -26,7 +24,7 @@ internal sealed class Executor : IDisposable, IStateflowsExecutor { public readonly Graph Graph; - public bool StateHasChanged = false; + public bool StateHasChanged; public StateMachinesRegister Register { get; set; } @@ -35,7 +33,7 @@ internal sealed class Executor : IDisposable, IStateflowsExecutor private readonly Stack ScopesStack = new Stack(); private EventStatus EventStatus; - private bool IsEventStatusOverriden = false; + private bool IsEventStatusOverriden; public void OverrideEventStatus(EventStatus eventStatus) { @@ -151,7 +149,7 @@ public async Task InitializeAsync(EventHolder event { await DoInitializeCascadeAsync(Graph.InitialVertex); - await DoCompletion(); + await DoCompletionAsync(); if (result == InitializationStatus.InitializedExplicitly) { @@ -373,9 +371,17 @@ private async Task DoProcessAsync(EventHolder event { if (eventHolder.Triggers(edge) && await DoGuardAsync(edge)) { + Context.AddExecutionStep( + edge.SourceName, + edge.TargetName == string.Empty + ? null + : edge.TargetName, + eventHolder.Payload + ); + await DoConsumeAsync(edge); - await DoCompletion(); + await DoCompletionAsync(); return EventStatus.Consumed; } @@ -645,7 +651,7 @@ private async Task DoConsumeAsync(Edge edge) } } - private async Task DoCompletion() + private async Task DoCompletionAsync() { var completionEventHolder = (new Completion()).ToEventHolder(); Context.SetEvent(completionEventHolder); diff --git a/Core/Stateflows/StateMachines/Engine/Inspector.cs b/Core/Stateflows/StateMachines/Engine/Inspector.cs index d3777e3b..2b784255 100644 --- a/Core/Stateflows/StateMachines/Engine/Inspector.cs +++ b/Core/Stateflows/StateMachines/Engine/Inspector.cs @@ -324,7 +324,7 @@ public async Task OnStateMachineInitializationExceptionAsync(StateMachineI if (ShouldPropagateException(context.Context.Executor.Graph, handled)) { - context.Context.Exceptions.Add(exception); + context.Context.AddException(exception); } return handled; @@ -342,7 +342,7 @@ public async Task OnStateMachineFinalizationExceptionAsync(StateMachineAct if (ShouldPropagateException(context.Context.Executor.Graph, handled)) { - context.Context.Exceptions.Add(exception); + context.Context.AddException(exception); } return handled; @@ -361,7 +361,7 @@ public async Task OnTransitionGuardExceptionAsync(GuardContext OnTransitionEffectExceptionAsync(TransitionConte if (ShouldPropagateException(context.Context.Executor.Graph, handled)) { - context.Context.Exceptions.Add(exception); + context.Context.AddException(exception); } return handled; @@ -398,7 +398,7 @@ public async Task OnStateInitializeExceptionAsync(StateActionContext conte if (ShouldPropagateException(context.Context.Executor.Graph, handled)) { - context.Context.Exceptions.Add(exception); + context.Context.AddException(exception); } return handled; @@ -416,7 +416,7 @@ public async Task OnStateFinalizeExceptionAsync(StateActionContext context if (ShouldPropagateException(context.Context.Executor.Graph, handled)) { - context.Context.Exceptions.Add(exception); + context.Context.AddException(exception); } return handled; @@ -434,7 +434,7 @@ public async Task OnStateEntryExceptionAsync(StateActionContext context, E if (ShouldPropagateException(context.Context.Executor.Graph, handled)) { - context.Context.Exceptions.Add(exception); + context.Context.AddException(exception); } return handled; @@ -452,7 +452,7 @@ public async Task OnStateExitExceptionAsync(StateActionContext context, Ex if (ShouldPropagateException(context.Context.Executor.Graph, handled)) { - context.Context.Exceptions.Add(exception); + context.Context.AddException(exception); } return handled; diff --git a/Core/Stateflows/StateMachines/Engine/Plugins/Exceptions.cs b/Core/Stateflows/StateMachines/Engine/Plugins/Exceptions.cs index 172d10a9..bee334e7 100644 --- a/Core/Stateflows/StateMachines/Engine/Plugins/Exceptions.cs +++ b/Core/Stateflows/StateMachines/Engine/Plugins/Exceptions.cs @@ -12,38 +12,38 @@ public Exceptions(IStateMachineLocator locator) this.locator = locator; } - private Task HandleException(StateMachineId stateMachineId, Exception exception) + private Task HandleExceptionAsync(StateMachineId stateMachineId, Exception exception) { if (locator.TryLocateStateMachine(stateMachineId, out var stateMachine)) { stateMachine.SendAsync(exception); } - + return Task.FromResult(true); } public override Task OnStateMachineInitializationExceptionAsync(IStateMachineInitializationContext context, Exception exception) - => HandleException(context.StateMachine.Id, exception); + => HandleExceptionAsync(context.StateMachine.Id, exception); public override Task OnStateMachineFinalizationExceptionAsync(IStateMachineActionContext context, Exception exception) - => HandleException(context.StateMachine.Id, exception); + => HandleExceptionAsync(context.StateMachine.Id, exception); public override Task OnTransitionGuardExceptionAsync(ITransitionContext context, Exception exception) - => HandleException(context.StateMachine.Id, exception); + => HandleExceptionAsync(context.StateMachine.Id, exception); public override Task OnTransitionEffectExceptionAsync(ITransitionContext context, Exception exception) - => HandleException(context.StateMachine.Id, exception); + => HandleExceptionAsync(context.StateMachine.Id, exception); public override Task OnStateInitializationExceptionAsync(IStateActionContext context, Exception exception) - => HandleException(context.StateMachine.Id, exception); + => HandleExceptionAsync(context.StateMachine.Id, exception); public override Task OnStateFinalizationExceptionAsync(IStateActionContext context, Exception exception) - => HandleException(context.StateMachine.Id, exception); + => HandleExceptionAsync(context.StateMachine.Id, exception); public override Task OnStateEntryExceptionAsync(IStateActionContext context, Exception exception) - => HandleException(context.StateMachine.Id, exception); + => HandleExceptionAsync(context.StateMachine.Id, exception); public override Task OnStateExitExceptionAsync(IStateActionContext context, Exception exception) - => HandleException(context.StateMachine.Id, exception); + => HandleExceptionAsync(context.StateMachine.Id, exception); } } diff --git a/Core/Stateflows/StateMachines/Engine/Processor.cs b/Core/Stateflows/StateMachines/Engine/Processor.cs index 6678be28..cd580410 100644 --- a/Core/Stateflows/StateMachines/Engine/Processor.cs +++ b/Core/Stateflows/StateMachines/Engine/Processor.cs @@ -160,7 +160,7 @@ Executor executor try { var attributes = eventHolder.GetType().GetCustomAttributes(); - if (!executor.Initialized && !attributes.Any()) + if (!executor.Initialized && !attributes.Any() && !typeof(Exception).IsAssignableFrom(eventHolder.PayloadType)) { result = await executor.InitializeAsync(eventHolder); } diff --git a/Core/Stateflows/StateMachines/Exceptions/StateMachineOverrideException.cs b/Core/Stateflows/StateMachines/Exceptions/StateMachineOverrideException.cs index 771b1905..7554942b 100644 --- a/Core/Stateflows/StateMachines/Exceptions/StateMachineOverrideException.cs +++ b/Core/Stateflows/StateMachines/Exceptions/StateMachineOverrideException.cs @@ -2,10 +2,9 @@ namespace Stateflows.StateMachines.Exceptions { -#pragma warning disable S3925 // "ISerializable" should be implemented correctly internal class StateMachineOverrideException : StateMachineDefinitionException { - public StateMachineClass StateMachineClass { get; } + new public StateMachineClass StateMachineClass { get; } public StateMachineOverrideException(string message, StateMachineClass stateMachineClass) : base(message, stateMachineClass.BehaviorClass) { @@ -17,5 +16,4 @@ public StateMachineOverrideException(string message, StateMachineClass stateMach StateMachineClass = stateMachineClass; } } -#pragma warning restore S3925 // "ISerializable" should be implemented correctly } diff --git a/Core/Stateflows/StateMachines/Interfaces/Context/IExecutionContext.cs b/Core/Stateflows/StateMachines/Interfaces/Context/IExecutionContext.cs new file mode 100644 index 00000000..af87aab2 --- /dev/null +++ b/Core/Stateflows/StateMachines/Interfaces/Context/IExecutionContext.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace Stateflows.StateMachines +{ + public interface IExecutionContext : Common.IExecutionContext + { + IEnumerable ExecutionSteps { get; } + } +} diff --git a/Core/Stateflows/StateMachines/Interfaces/Context/IExecutionStep.cs b/Core/Stateflows/StateMachines/Interfaces/Context/IExecutionStep.cs new file mode 100644 index 00000000..6c80630d --- /dev/null +++ b/Core/Stateflows/StateMachines/Interfaces/Context/IExecutionStep.cs @@ -0,0 +1,11 @@ +namespace Stateflows.StateMachines +{ + public interface IExecutionStep + { + string SourceStateName { get; } + + string TargetStateName { get; } + + object TransitionTrigger { get; } + } +} diff --git a/Core/Stateflows/StateMachines/Models/Edge.cs b/Core/Stateflows/StateMachines/Models/Edge.cs index 5d5ca656..d3b17e43 100644 --- a/Core/Stateflows/StateMachines/Models/Edge.cs +++ b/Core/Stateflows/StateMachines/Models/Edge.cs @@ -40,9 +40,9 @@ internal class Edge public EdgeType Type { get; set; } public bool IsElse { get; set; } - public Logic Guards { get; } = new Logic() { Name = Constants.Guard }; + public Logic Guards { get; } = new Logic(Constants.Guard); - public Logic Effects { get; } = new Logic() { Name = Constants.Effect }; + public Logic Effects { get; } = new Logic(Constants.Effect); public string SourceName { get; set; } public Vertex Source { get; set; } diff --git a/Core/Stateflows/StateMachines/Models/Graph.cs b/Core/Stateflows/StateMachines/Models/Graph.cs index 030076c6..c8524926 100644 --- a/Core/Stateflows/StateMachines/Models/Graph.cs +++ b/Core/Stateflows/StateMachines/Models/Graph.cs @@ -2,19 +2,19 @@ using System.Linq; using System.Diagnostics; using System.Collections.Generic; +using Stateflows.Common; +using Stateflows.Common.Models; +using Stateflows.Common.Registration.Builders; +using Stateflows.StateMachines.Exceptions; using Stateflows.StateMachines.Interfaces; using Stateflows.StateMachines.Registration; using Stateflows.StateMachines.Registration.Interfaces; -using Stateflows.Common.Models; -using Stateflows.StateMachines.Exceptions; -using Stateflows.Common.Registration.Builders; -using Stateflows.Common; namespace Stateflows.StateMachines.Models { internal class Graph { - internal readonly StateflowsBuilder StateflowsBuilder = null; + internal readonly StateflowsBuilder StateflowsBuilder; public Dictionary InitCounter = new Dictionary(); @@ -39,14 +39,10 @@ public Graph(string name, int version, StateflowsBuilder stateflowsBuilder) public readonly Dictionary> Initializers = new Dictionary>(); public readonly List InitializerTypes = new List(); - public Logic DefaultInitializer = null; + public Logic DefaultInitializer; - private Logic finalize = null; - public Logic Finalize - => finalize ??= new Logic() - { - Name = Constants.Finalize - }; + public Logic Finalize { get; } = + new Logic(Constants.Finalize); public readonly List ExceptionHandlerFactories = new List(); @@ -132,7 +128,7 @@ public void Build() foreach (var edge in AllEdges) { - if (edge.TargetName != null && edge.TargetName != Constants.DefaultTransitionTarget) + if (!string.IsNullOrEmpty(edge.TargetName) && string.Compare(edge.TargetName, Constants.DefaultTransitionTarget) != 0) { if (AllVertices.TryGetValue(edge.TargetName, out var target)) { diff --git a/Core/Stateflows/StateMachines/Models/Vertex.cs b/Core/Stateflows/StateMachines/Models/Vertex.cs index 2d9acc01..98ad97ac 100644 --- a/Core/Stateflows/StateMachines/Models/Vertex.cs +++ b/Core/Stateflows/StateMachines/Models/Vertex.cs @@ -30,16 +30,16 @@ internal class Vertex public string Identifier => Name; private Logic initialize = null; - public Logic Initialize => initialize ??= new Logic() { Name = Constants.Initialize }; + public Logic Initialize => initialize ??= new Logic(Constants.Initialize); private Logic finalize = null; - public Logic Finalize => finalize ??= new Logic() { Name = Constants.Finalize }; + public Logic Finalize => finalize ??= new Logic(Constants.Finalize); private Logic entry = null; - public Logic Entry => entry ??= new Logic() { Name = Constants.Entry }; + public Logic Entry => entry ??= new Logic(Constants.Entry); private Logic exit = null; - public Logic Exit => exit ??= new Logic() { Name = Constants.Exit }; + public Logic Exit => exit ??= new Logic(Constants.Exit); public Dictionary Edges { get; set; } = new Dictionary(); public IEnumerable OrderedEdges => Edges.Values.OrderBy(edge => edge.IsElse); diff --git a/Core/Stateflows/StateMachines/Registration/Builders/StateMachineBuilder.cs b/Core/Stateflows/StateMachines/Registration/Builders/StateMachineBuilder.cs index f3ab70d0..22afe738 100644 --- a/Core/Stateflows/StateMachines/Registration/Builders/StateMachineBuilder.cs +++ b/Core/Stateflows/StateMachines/Registration/Builders/StateMachineBuilder.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; using Stateflows.Common; -using Stateflows.Common.Exceptions; using Stateflows.Common.Models; using Stateflows.Common.Extensions; using Stateflows.Common.Registration; @@ -47,10 +46,7 @@ public IInitializedStateMachineBuilder AddInitializer(Type initializerType, stri { if (!Result.Initializers.TryGetValue(initializerName, out var initializer)) { - initializer = new Logic() - { - Name = Constants.Initialize - }; + initializer = new Logic(Constants.Initialize); Result.Initializers.Add(initializerName, initializer); Result.InitializerTypes.Add(initializerType); @@ -63,10 +59,7 @@ public IInitializedStateMachineBuilder AddInitializer(Type initializerType, stri public IInitializedStateMachineBuilder AddDefaultInitializer(Func> actionAsync) { - Result.DefaultInitializer = new Logic() - { - Name = Constants.Initialize - }; + Result.DefaultInitializer = new Logic(Constants.Initialize); Result.DefaultInitializer.Actions.Add(c => { diff --git a/Core/Stateflows/StateMachines/StateMachinesDependencyInjection.cs b/Core/Stateflows/StateMachines/StateMachinesDependencyInjection.cs index b6aa90ac..6c4b36b5 100644 --- a/Core/Stateflows/StateMachines/StateMachinesDependencyInjection.cs +++ b/Core/Stateflows/StateMachines/StateMachinesDependencyInjection.cs @@ -73,6 +73,10 @@ private static StateMachinesRegister EnsureStateMachinesServices(this IStateflow StateMachinesContextHolder.TransitionContext.Value ?? throw new InvalidOperationException($"No service for type '{typeof(ITransitionContext).FullName}' is available in this context.") ) + .AddTransient(provider => + StateMachinesContextHolder.ExecutionContext.Value ?? + throw new InvalidOperationException($"No service for type '{typeof(IExecutionContext).FullName}' is available in this context.") + ) ; } diff --git a/Examples/Blazor/WebAssembly/Server/Program.cs b/Examples/Blazor/WebAssembly/Server/Program.cs index 1f3fbfe2..561d5159 100644 --- a/Examples/Blazor/WebAssembly/Server/Program.cs +++ b/Examples/Blazor/WebAssembly/Server/Program.cs @@ -14,6 +14,23 @@ .AddPlantUml() .AddStateMachines(b => b + .AddStateMachine("zemanowa-maszyna", b => b + .AddInitialState("draft", b => b + .AddTransition("active") + ) + .AddCompositeState("active", b => b + .AddInitialState("normal", b => b + .AddTransition("special") + ) + .AddState("special", b => b + .AddTransition("normal") + .AddInternalTransition(b => b + .AddEffect(async c => { }) + ) + ) + ) + ) + .AddStateMachine("stateMachine1", b => b .AddInitialState("state1", b => b .AddTransition("state2") @@ -22,6 +39,10 @@ ) ) .AddState("state2", b => b + .AddOnEntry(async c => + { + // logic + }) .AddTransition("state3", b => b .AddGuard(c => c.Event.AnswerToLifeUniverseAndEverything == 42) ) diff --git a/Tests/Activity.IntegrationTests/Tests/Exceptions.cs b/Tests/Activity.IntegrationTests/Tests/Exceptions.cs index 76bd0e55..1a0a3219 100644 --- a/Tests/Activity.IntegrationTests/Tests/Exceptions.cs +++ b/Tests/Activity.IntegrationTests/Tests/Exceptions.cs @@ -25,6 +25,7 @@ public class Exceptions : StateflowsTestClass private bool Executed2 = false; private bool Executed3 = false; private bool ExceptionHandlerOutput = false; + private int ExceptionHandlerOutputCount = 0; private static string Value1 = "boo"; private static string Value2 = "boo"; private static string Value3 = "boo"; @@ -279,21 +280,71 @@ protected override void InitializeStateflows(IStateflowsBuilder builder) ExceptionHandlerOutput = c.GetTokensOfType().Any(); }) ) - //.AddActivity("global", b => b - // .AddExceptionHandler(async c => - // { - // Executed1 = true; - // Value1 = c.NodeOfOrigin.NodeName; - // Value2 = c.ProtectedNode.NodeName; - // }) - // .AddInitial(b => b - // .AddControlFlow("faulty") - // ) - // .AddAction( - // "faulty", - // async c => throw new Exception() - // ) - //) + .AddActivity("structured-output-merge", b => b + .AddInitial(b => b + .AddControlFlow("main") + ) + .AddStructuredActivity("main", b => b + .AddExceptionHandler(async c => + { + c.Output(c.Exception.Message); + }) + .AddInitial(b => b + .AddControlFlow("action1") + ) + .AddAction("action1", async c => + { + c.Output("test"); + throw new Exception("test"); + }, b => b + .AddFlow() + ) + .AddOutput() + + .AddFlow("final") + ) + .AddAction("final", async c => + { + ExceptionHandlerOutputCount = c.GetTokensOfType().Count(); + }) + ) + .AddActivity("output-merge", b => b + .AddInitial(b => b + .AddControlFlow("action1") + ) + .AddAction("action1", + async c => + { + c.Output("test"); + throw new Exception("test"); + }, + b => b + .AddFlow("final") + .AddExceptionHandler(async c => + { + c.Output(c.Exception.Message); + }) + ) + .AddAction("final", async c => + { + ExceptionHandlerOutputCount = c.GetTokensOfType().Count(); + }) + ) + //.AddActivity("global", b => b + // .AddExceptionHandler(async c => + // { + // Executed1 = true; + // Value1 = c.NodeOfOrigin.NodeName; + // Value2 = c.ProtectedNode.NodeName; + // }) + // .AddInitial(b => b + // .AddControlFlow("faulty") + // ) + // .AddAction( + // "faulty", + // async c => throw new Exception() + // ) + //) ) ; } @@ -425,6 +476,28 @@ public async Task ExceptionHandledWithOutput() Assert.IsTrue(ExceptionHandlerOutput); } + [TestMethod] + public async Task ExceptionHandledInStructureWithMergedOutput() + { + if (ActivityLocator.TryLocateActivity(new ActivityId("structured-output-merge", "x"), out var a)) + { + await a.SendAsync(new Initialize()); + } + + Assert.AreEqual(2, ExceptionHandlerOutputCount); + } + + [TestMethod] + public async Task ExceptionHandledWithMergedOutput() + { + if (ActivityLocator.TryLocateActivity(new ActivityId("output-merge", "x"), out var a)) + { + await a.SendAsync(new Initialize()); + } + + Assert.AreEqual(2, ExceptionHandlerOutputCount); + } + //[TestMethod] //public async Task ExceptionHandledGlobally() //{ diff --git a/Tests/StateMachine/StateMachine.IntegrationTests/Classes/StateMachines/TypedStateMachine.cs b/Tests/StateMachine/StateMachine.IntegrationTests/Classes/StateMachines/TypedStateMachine.cs index a9defc2c..d3f2c6f9 100644 --- a/Tests/StateMachine/StateMachine.IntegrationTests/Classes/StateMachines/TypedStateMachine.cs +++ b/Tests/StateMachine/StateMachine.IntegrationTests/Classes/StateMachines/TypedStateMachine.cs @@ -1,6 +1,6 @@ -using Stateflows.StateMachines; +using Stateflows.Common; +using Stateflows.StateMachines; using Stateflows.StateMachines.Attributes; -using Stateflows.Common; using StateMachine.IntegrationTests.Classes.Transitions; namespace StateMachine.IntegrationTests.Classes.StateMachines @@ -33,8 +33,8 @@ internal class StateA : IStateEntry, IStateExit { private readonly IStateContext stateContext; private readonly IStateMachineContext stateMachineContext; - private readonly IExecutionContext executionContext; - public StateA(IStateContext stateContext, IStateMachineContext stateMachineContext, IExecutionContext executionContext) + private readonly Stateflows.StateMachines.IExecutionContext executionContext; + public StateA(IStateContext stateContext, IStateMachineContext stateMachineContext, Stateflows.StateMachines.IExecutionContext executionContext) { this.stateContext = stateContext; this.stateMachineContext = stateMachineContext; @@ -64,12 +64,12 @@ internal class SomeTransition : ITransitionGuard, ITransitionEffect private readonly IStateMachineContext stateMachineContext; private readonly ITransitionContext transitionContext; - private readonly IExecutionContext executionContext; + private readonly Stateflows.StateMachines.IExecutionContext executionContext; public SomeEventGuard1( IStateMachineContext stateMachineContext, - ITransitionContext transitionContext, - IExecutionContext executionContext + ITransitionContext transitionContext, + Stateflows.StateMachines.IExecutionContext executionContext ) { this.stateMachineContext = stateMachineContext; diff --git a/Tests/StateMachine/StateMachine.IntegrationTests/Classes/Transitions/SomeEventGuard2.cs b/Tests/StateMachine/StateMachine.IntegrationTests/Classes/Transitions/SomeEventGuard2.cs index 78f4cece..98e9f95a 100644 --- a/Tests/StateMachine/StateMachine.IntegrationTests/Classes/Transitions/SomeEventGuard2.cs +++ b/Tests/StateMachine/StateMachine.IntegrationTests/Classes/Transitions/SomeEventGuard2.cs @@ -10,11 +10,11 @@ internal class SomeEventGuard2 : ITransitionGuard private readonly IStateMachineContext stateMachineContext; private readonly ITransitionContext transitionContext; - private readonly IExecutionContext executionContext; + private readonly Stateflows.StateMachines.IExecutionContext executionContext; public SomeEventGuard2( IStateMachineContext stateMachineContext, - ITransitionContext transitionContext, - IExecutionContext executionContext + ITransitionContext transitionContext, + Stateflows.StateMachines.IExecutionContext executionContext ) { this.stateMachineContext = stateMachineContext; diff --git a/Tests/StateMachine/StateMachine.IntegrationTests/Classes/Transitions/SomeEventGuard3.cs b/Tests/StateMachine/StateMachine.IntegrationTests/Classes/Transitions/SomeEventGuard3.cs index c997236f..a9d18aae 100644 --- a/Tests/StateMachine/StateMachine.IntegrationTests/Classes/Transitions/SomeEventGuard3.cs +++ b/Tests/StateMachine/StateMachine.IntegrationTests/Classes/Transitions/SomeEventGuard3.cs @@ -10,11 +10,11 @@ internal class SomeEventGuard3 : ITransitionGuard private readonly IStateMachineContext stateMachineContext; private readonly ITransitionContext transitionContext; - private readonly IExecutionContext executionContext; + private readonly Stateflows.StateMachines.IExecutionContext executionContext; public SomeEventGuard3( IStateMachineContext stateMachineContext, - ITransitionContext transitionContext, - IExecutionContext executionContext + ITransitionContext transitionContext, + Stateflows.StateMachines.IExecutionContext executionContext ) { this.stateMachineContext = stateMachineContext; diff --git a/Tests/StateMachine/StateMachine.IntegrationTests/Classes/Transitions/SomeEventTransition.cs b/Tests/StateMachine/StateMachine.IntegrationTests/Classes/Transitions/SomeEventTransition.cs index 6fc25022..248630a9 100644 --- a/Tests/StateMachine/StateMachine.IntegrationTests/Classes/Transitions/SomeEventTransition.cs +++ b/Tests/StateMachine/StateMachine.IntegrationTests/Classes/Transitions/SomeEventTransition.cs @@ -10,11 +10,11 @@ internal class SomeEventTransition : ITransitionGuard, ITransitionEff private readonly IStateMachineContext stateMachineContext; private readonly ITransitionContext transitionContext; - private readonly IExecutionContext executionContext; + private readonly Stateflows.StateMachines.IExecutionContext executionContext; public SomeEventTransition( IStateMachineContext stateMachineContext, - ITransitionContext transitionContext, - IExecutionContext executionContext + ITransitionContext transitionContext, + Stateflows.StateMachines.IExecutionContext executionContext ) { this.stateMachineContext = stateMachineContext; diff --git a/Tests/StateMachine/StateMachine.IntegrationTests/Tests/Activities.cs b/Tests/StateMachine/StateMachine.IntegrationTests/Tests/Activities.cs index da4739ea..71622935 100644 --- a/Tests/StateMachine/StateMachine.IntegrationTests/Tests/Activities.cs +++ b/Tests/StateMachine/StateMachine.IntegrationTests/Tests/Activities.cs @@ -1,6 +1,5 @@ using Stateflows.Activities; using StateMachine.IntegrationTests.Utils; -using Stateflows.Activities; namespace StateMachine.IntegrationTests.Tests { diff --git a/Tests/StateMachine/StateMachine.IntegrationTests/Tests/Concurrency.cs b/Tests/StateMachine/StateMachine.IntegrationTests/Tests/Concurrency.cs index 99b551f8..2476310d 100644 --- a/Tests/StateMachine/StateMachine.IntegrationTests/Tests/Concurrency.cs +++ b/Tests/StateMachine/StateMachine.IntegrationTests/Tests/Concurrency.cs @@ -58,10 +58,6 @@ protected override void InitializeStateflows(IStateflowsBuilder builder) [TestMethod] public async Task TwoConcurrentBehaviors() { - var initialized = false; - string currentState1 = ""; - var someStatus1 = EventStatus.Rejected; - if (StateMachineLocator.TryLocateStateMachine(new StateMachineId("a", "x"), out var a) && StateMachineLocator.TryLocateStateMachine(new StateMachineId("b", "x"), out var b)) { @@ -84,10 +80,6 @@ await Task.WhenAll( [TestMethod] public async Task TwoConcurrentInstances() { - var initialized = false; - string currentState1 = ""; - var someStatus1 = EventStatus.Rejected; - if (StateMachineLocator.TryLocateStateMachine(new StateMachineId("instance", "a"), out var a) && StateMachineLocator.TryLocateStateMachine(new StateMachineId("instance", "b"), out var b)) { diff --git a/Tests/StateMachine/StateMachine.IntegrationTests/Tests/Subscription.cs b/Tests/StateMachine/StateMachine.IntegrationTests/Tests/Subscription.cs index d04ec44a..aa930f3e 100644 --- a/Tests/StateMachine/StateMachine.IntegrationTests/Tests/Subscription.cs +++ b/Tests/StateMachine/StateMachine.IntegrationTests/Tests/Subscription.cs @@ -55,10 +55,6 @@ public async Task SubscribingSuccessful() StateMachineLocator.TryLocateStateMachine(new StateMachineId("subscribee", "x"), out var subscribee) ) { - //await subscriber.SendAsync(new Initialize()); - - //await subscribee.SendAsync(new Initialize()); - await subscribee.SendAsync(new OtherEvent()); await subscriber.SendAsync(new OtherEvent()); @@ -88,8 +84,6 @@ public async Task WatchSuccessful() } }); - //await subscribee.InitializeAsync(); - await subscribee.SendAsync(new OtherEvent()); await subscribee.GetCurrentStateAsync(); diff --git a/Tests/StateMachine/StateMachine.IntegrationTests/Tests/Transitions.cs b/Tests/StateMachine/StateMachine.IntegrationTests/Tests/Transitions.cs index ec303aff..411ea29f 100644 --- a/Tests/StateMachine/StateMachine.IntegrationTests/Tests/Transitions.cs +++ b/Tests/StateMachine/StateMachine.IntegrationTests/Tests/Transitions.cs @@ -34,7 +34,7 @@ protected override void InitializeStateflows(IStateflowsBuilder builder) ) ) .AddState("state2", b => b - .AddOnEntry(c => StateEntered = true) + .AddOnEntry(c => StateEntered = c.ExecutionSteps.LastOrDefault()?.SourceStateName == "state1") ) )