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
32 changes: 16 additions & 16 deletions src/Stateless/Graph/GraphStyleBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,11 @@ public abstract class GraphStyleBase
/// <returns>Description of all transitions, in the desired format.</returns>
public virtual List<string> FormatAllTransitions(List<Transition> transitions)
{
List<string> lines = new List<string>();
if (transitions == null) return lines;
if (transitions == null)
return new List<string>();

// Eagerly set the initial capacity to minimize re-allocation of internal array.
List<string> lines = new List<string>(transitions.Count);

foreach (var transit in transitions)
{
Expand All @@ -84,26 +87,23 @@ public virtual List<string> FormatAllTransitions(List<Transition> transitions)
stay.SourceState.NodeName, stay.Guards.Select(x => x.Description));
}
}
else
else if (transit is FixedTransition fix)
{
if (transit is FixedTransition fix)
{
line = FormatOneTransition(fix.SourceState.NodeName, fix.Trigger.UnderlyingTrigger.ToString(),
line = FormatOneTransition(fix.SourceState.NodeName, fix.Trigger.UnderlyingTrigger.ToString(),
fix.DestinationEntryActions.Select(x => x.Method.Description),
fix.DestinationState.NodeName, fix.Guards.Select(x => x.Description));
}
else
{
if (transit is DynamicTransition dyn)
{
line = FormatOneTransition(dyn.SourceState.NodeName, dyn.Trigger.UnderlyingTrigger.ToString(),
}
else if (transit is DynamicTransition dyn)
{
line = FormatOneTransition(dyn.SourceState.NodeName, dyn.Trigger.UnderlyingTrigger.ToString(),
dyn.DestinationEntryActions.Select(x => x.Method.Description),
dyn.DestinationState.NodeName, new List<string> { dyn.Criterion });
}
else
throw new ArgumentException("Unexpected transition type");
}
}
else
{
throw new ArgumentException("Unexpected transition type");
}

if (line != null)
lines.Add(line);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Stateless/ParameterConversion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public static object Unpack(object[] args, Type argType, int index)
if (args.Length == 0)
return null;

if (args.Length <= index)
if (args.Length <= index || index < 0)
throw new ArgumentException(
string.Format(ParameterConversionResources.ArgOfTypeRequiredInPosition, argType, index));

Expand Down
13 changes: 6 additions & 7 deletions src/Stateless/Reflection/DynamicTransitionInfo.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;

namespace Stateless.Reflection
{
Expand All @@ -19,12 +20,12 @@ public DynamicStateInfo(string destinationState, string criterion)
/// <summary>
/// The name of the destination state
/// </summary>
public string DestinationState { get; set; }
public string DestinationState { get; }

/// <summary>
/// The reason this destination state was chosen
/// </summary>
public string Criterion { get; set; }
public string Criterion { get; }
}

/// <summary>
Expand Down Expand Up @@ -80,15 +81,13 @@ public class DynamicTransitionInfo : TransitionInfo
public static DynamicTransitionInfo Create<TTrigger>(TTrigger trigger, IEnumerable<InvocationInfo> guards,
InvocationInfo selector, DynamicStateInfos possibleStates)
{
var transition = new DynamicTransitionInfo
return new DynamicTransitionInfo
{
Trigger = new TriggerInfo(trigger),
GuardConditionsMethodDescriptions = guards ?? new List<InvocationInfo>(),
GuardConditionsMethodDescriptions = guards ?? Array.Empty<InvocationInfo>(),
DestinationStateSelectorDescription = selector,
PossibleDestinationStates = possibleStates // behaviour.PossibleDestinationStates?.Select(x => x.ToString()).ToArray()
};

return transition;
}

private DynamicTransitionInfo() { }
Expand Down
8 changes: 3 additions & 5 deletions src/Stateless/Reflection/FixedTransitionInfo.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System;
using System.Linq;

namespace Stateless.Reflection
Expand All @@ -10,16 +10,14 @@ public class FixedTransitionInfo : TransitionInfo
{
internal static FixedTransitionInfo Create<TState, TTrigger>(StateMachine<TState, TTrigger>.TriggerBehaviour behaviour, StateInfo destinationStateInfo)
{
var transition = new FixedTransitionInfo
return new FixedTransitionInfo
{
Trigger = new TriggerInfo(behaviour.Trigger),
DestinationState = destinationStateInfo,
GuardConditionsMethodDescriptions = behaviour.Guard == null
? new List<InvocationInfo>() : behaviour.Guard.Conditions.Select(c => c.MethodDescription),
? Array.Empty<InvocationInfo>() : behaviour.Guard.Conditions.Select(c => c.MethodDescription),
IsInternalTransition = behaviour is StateMachine<TState, TTrigger>.InternalTriggerBehaviour
};

return transition;
}

private FixedTransitionInfo() { }
Expand Down
4 changes: 2 additions & 2 deletions src/Stateless/Reflection/IgnoredTransitionInfo.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Collections.Generic;
using System;
using System.Linq;

namespace Stateless.Reflection
Expand All @@ -14,7 +14,7 @@ internal static IgnoredTransitionInfo Create<TState, TTrigger>(StateMachine<TSta
{
Trigger = new TriggerInfo(behaviour.Trigger),
GuardConditionsMethodDescriptions = behaviour.Guard == null
? new List<InvocationInfo>() : behaviour.Guard.Conditions.Select(c => c.MethodDescription)
? Array.Empty<InvocationInfo>() : behaviour.Guard.Conditions.Select(c => c.MethodDescription)
};

return transition;
Expand Down
4 changes: 3 additions & 1 deletion src/Stateless/Reflection/InvocationInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ namespace Stateless.Reflection
/// </summary>
public class InvocationInfo
{
private static readonly char[] methodNameChars = { '<', '>', '`' };

readonly string _description; // _description can be null if user didn't specify a description

/// <summary>
Expand Down Expand Up @@ -65,7 +67,7 @@ public string Description
return _description;
if (MethodName == null)
return SpecialConstants.NullString;
if (MethodName.IndexOfAny(new char[] { '<', '>', '`' }) >= 0)
if (MethodName.IndexOfAny(methodNameChars) >= 0)
return DefaultFunctionDescription;
return MethodName;
}
Expand Down
76 changes: 76 additions & 0 deletions test/Stateless.Tests/ParameterConversionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System;
using Xunit;

namespace Stateless.Tests
{
public class ParameterConversionTests
{
private readonly object[] args = { 5, 10, 15 };

[Fact]
public void Unpack_ShouldReturnArg_WhenValidationSucceeds()
{
Assert.Equal(5, ParameterConversion.Unpack(args, typeof(int), 0));
Assert.Equal(10, ParameterConversion.Unpack(args, typeof(int), 1));
Assert.Equal(15, ParameterConversion.Unpack(args, typeof(int), 2));
Assert.Null(ParameterConversion.Unpack(new object[] { "John", null, "Doe" }, typeof(string), 1));
}

[Fact]
public void Unpack_ShouldThrowArgumentNullException_WhenArgsIsNull()
{
Assert.Throws<ArgumentNullException>(() => ParameterConversion.Unpack(null, null, 0));
}

[Fact]
public void Unpack_ShouldReturnNull_WhenArgsLengthIsZero()
{
Assert.Null(ParameterConversion.Unpack(Array.Empty<object>(), null, 0));
}

[Fact]
public void Unpack_ShouldThrowArgumentException_WhenArgsLengthLessThanIndex()
{
Assert.Throws<ArgumentException>(() => ParameterConversion.Unpack(args, typeof(int), 5));
}

[Fact]
public void Unpack_ShouldThrowArgumentException_WhenIndexLessThanZero()
{
Assert.Throws<ArgumentException>(() => ParameterConversion.Unpack(args, typeof(int), -1));
}

[Fact]
public void Unpack_ShouldThrowArgumentException_WhenTypeIsNotAssignable()
{
Assert.Throws<ArgumentException>(() => ParameterConversion.Unpack(args, typeof(char), 0));
}

[Fact]
public void Unpack_ShouldReturnDefault_When2ParameterMethodCalled()
{
Assert.Equal(0, ParameterConversion.Unpack<int>(Array.Empty<object>(), 0));
Assert.Equal(char.MinValue, ParameterConversion.Unpack<char>(Array.Empty<object>(), 0));
Assert.Equal(false, ParameterConversion.Unpack<bool>(Array.Empty<object>(), 0));
}

[Fact]
public void Validate_ShouldThrowArgumentException_WhenArgsGreaterThanExpected()
{
Assert.Throws<ArgumentException>(() => ParameterConversion.Validate(args, new Type[] { null, null }));
}

[Fact]
public void Validate_ShouldWorkWithoutException_WhenEverythingExpectedIsProvided()
{
try
{
ParameterConversion.Validate(args, new Type[] { typeof(int), typeof(int), typeof(int) });
}
catch (Exception)
{
throw;
}
}
}
}
Loading