Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/Stateless/Reflection/FixedTransitionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@ internal static FixedTransitionInfo Create<TState, TTrigger>(StateMachine<TState
};
}

internal static FixedTransitionInfo Create<TState, TTrigger>(StateMachine<TState, TTrigger>.TriggerBehaviourAsync behaviour, StateInfo destinationStateInfo)
{
return new FixedTransitionInfo
{
Trigger = new TriggerInfo(behaviour.Trigger),
DestinationState = destinationStateInfo,
GuardConditionsMethodDescriptions = behaviour.Guard == null
? Array.Empty<InvocationInfo>() : behaviour.Guard.Conditions.Select(c => c.MethodDescription),
IsInternalTransition = behaviour is StateMachine<TState, TTrigger>.InternalTriggerBehaviour
};
}

private FixedTransitionInfo() { }

/// <summary>
Expand Down
16 changes: 16 additions & 0 deletions src/Stateless/Reflection/StateInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ internal static void AddRelationships<TState, TTrigger>(StateInfo info, StateMac
var fixedTransitions = new List<FixedTransitionInfo>();
var dynamicTransitions = new List<DynamicTransitionInfo>();

// Sync triggers
foreach (var triggerBehaviours in stateRepresentation.TriggerBehaviours)
{
// First add all the deterministic transitions
Expand Down Expand Up @@ -78,6 +79,21 @@ internal static void AddRelationships<TState, TTrigger>(StateInfo info, StateMac
}
}

// Async triggers
foreach (var triggerBehaviour in stateRepresentation.TriggerBehavioursAsync.Values.SelectMany(x => x))
{
if (triggerBehaviour is StateMachine<TState, TTrigger>.TransitioningTriggerBehaviourAsync transitioningTriggerBehaviourAsync)
{
var destinationInfo = lookupState(transitioningTriggerBehaviourAsync.Destination);
fixedTransitions.Add(FixedTransitionInfo.Create(transitioningTriggerBehaviourAsync, destinationInfo));
}
else if (triggerBehaviour is StateMachine<TState, TTrigger>.ReentryTriggerBehaviourAsync reentryTriggerBehaviourAsync)
{
var destinationInfo = lookupState(reentryTriggerBehaviourAsync.Destination);
fixedTransitions.Add(FixedTransitionInfo.Create(reentryTriggerBehaviourAsync, destinationInfo));
}
}

info.AddRelationships(superstate, substates, fixedTransitions, dynamicTransitions);
}

Expand Down
2 changes: 2 additions & 0 deletions src/Stateless/StateMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ public StateMachineInfo GetInfo()

var behaviours = _stateConfiguration.SelectMany(kvp => kvp.Value.TriggerBehaviours.SelectMany(b => b.Value.OfType<TransitioningTriggerBehaviour>().Select(tb => tb.Destination))).ToList();
behaviours.AddRange(_stateConfiguration.SelectMany(kvp => kvp.Value.TriggerBehaviours.SelectMany(b => b.Value.OfType<ReentryTriggerBehaviour>().Select(tb => tb.Destination))).ToList());
behaviours.AddRange(_stateConfiguration.SelectMany(kvp => kvp.Value.TriggerBehavioursAsync.SelectMany(b => b.Value.OfType<TransitioningTriggerBehaviourAsync>().Select(tb => tb.Destination))).ToList());
behaviours.AddRange(_stateConfiguration.SelectMany(kvp => kvp.Value.TriggerBehavioursAsync.SelectMany(b => b.Value.OfType<ReentryTriggerBehaviourAsync>().Select(tb => tb.Destination))).ToList());

var reachable = behaviours
.Distinct()
Expand Down
32 changes: 25 additions & 7 deletions test/Stateless.Tests/GetInfoFixture.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Threading.Tasks;
using System.Linq;
using System.Threading.Tasks;
using Xunit;

namespace Stateless.Tests
Expand All @@ -12,31 +13,48 @@ public void GetInfo_should_return_Entry_action_with_trigger_name()
var sm = new StateMachine<State, Trigger>(State.A);
sm.Configure(State.B)
.OnEntryFrom(Trigger.X, () => { });

// ACT
var stateMachineInfo = sm.GetInfo();

// ASSERT
var stateInfo = Assert.Single(stateMachineInfo.States);
var entryActionInfo = Assert.Single(stateInfo.EntryActions);
Assert.Equal(Trigger.X.ToString(), entryActionInfo.FromTrigger);
}

[Fact]
public void GetInfo_should_return_async_Entry_action_with_trigger_name()
{
// ARRANGE
var sm = new StateMachine<State, Trigger>(State.A);
sm.Configure(State.B)
.OnEntryFromAsync(Trigger.X, () => Task.CompletedTask);

// ACT
var stateMachineInfo = sm.GetInfo();

// ASSERT
var stateInfo = Assert.Single(stateMachineInfo.States);
var entryActionInfo = Assert.Single(stateInfo.EntryActions);
Assert.Equal(Trigger.X.ToString(), entryActionInfo.FromTrigger);
}

[Fact]
public void GetInfo_should_include_async_Trigger()
{
// ARRANGE
var sm = new StateMachine<State, Trigger>(State.A);
sm.Configure(State.A)
.PermitIfAsync(Trigger.X, State.B, () => Task.FromResult(true));

// ACT
var stateMachineInfo = sm.GetInfo();

// ASSERT
var stateInfo = Assert.Single(stateMachineInfo.States.Where(x => x.UnderlyingState.Equals(State.A)));
var transitionInfo = Assert.Single(stateInfo.Transitions);
Assert.Equal(Trigger.X, transitionInfo.Trigger.UnderlyingTrigger);
}
}
}
}