Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nullable reference types support #61

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,12 @@ public static void BeSuccessfulTransitionResultWithNewState<TStates, TEvents>(th
.ForCondition(transitionResult.Fired)
.FailWith("expected successful (fired) transition result.");

Execute.Assertion
.ForCondition(transitionResult.NewState.CompareTo(expectedNewState.Id) == 0)
.FailWith("expected transition result with new state = `" + expectedNewState.Id + "`, but found `" + transitionResult.NewState + "`.");
if (transitionResult is FiredTransitionResult<TStates> fired)
{
Execute.Assertion
.ForCondition(fired.NewState.CompareTo(expectedNewState.Id) == 0)
.FailWith("expected transition result with new state = `" + expectedNewState.Id + "`, but found `" + fired.NewState + "`.");
}
}

public static void BeNotFiredTransitionResult<TStates>(this ObjectAssertions assertions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
namespace Appccelerate.StateMachine.Facts
{
using System;
using Appccelerate.StateMachine.Machine.Transitions;
using FluentAssertions.Execution;
using FluentAssertions.Primitives;
using StateMachine.Machine;
Expand All @@ -36,9 +37,12 @@ public static void BeSuccessfulTransitionResultWithNewState<TStates, TEvents>(th
.ForCondition(transitionResult.Fired)
.FailWith("expected successful (fired) transition result.");

Execute.Assertion
.ForCondition(transitionResult.NewState.CompareTo(expectedNewState.Id) == 0)
.FailWith("expected transition result with new state = `" + expectedNewState.Id + "`, but found `" + transitionResult.NewState + "`.");
if (transitionResult is FiredTransitionResult<TStates> fired)
{
Execute.Assertion
.ForCondition(fired.NewState.CompareTo(expectedNewState.Id) == 0)
.FailWith("expected transition result with new state = `" + expectedNewState.Id + "`, but found `" + fired.NewState + "`.");
}
}

public static void BeNotFiredTransitionResult<TStates>(this ObjectAssertions assertions)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>

<PropertyGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public void EventsQueueing(
"when firing an event onto the state machine".x(() =>
{
machine.Fire(FirstEvent);
machine.Fire(SecondEvent);
machine.Fire(SecondEvent, null);
machine.Start();
});

Expand Down
1 change: 1 addition & 0 deletions source/Appccelerate.StateMachine.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ArrangeThisQualifier/@EntryIndexedValue">DO_NOT_SHOW</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=05b40ed0_002De56c_002D4397_002D8789_002D13ce6a902f21/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static, Instance" AccessRightKinds="Private, Protected, ProtectedInternal, Internal, Public" Description="Machine.Specifications it"&gt;&lt;ElementKinds&gt;&lt;Kind Name="Machine.Specifications_Behavior" /&gt;&lt;Kind Name="Machine.Specifications_Context" /&gt;&lt;Kind Name="Machine.Specifications_ContextBase" /&gt;&lt;Kind Name="Machine.Specifications_Specification" /&gt;&lt;Kind Name="Machine.Specifications_SupportingField" /&gt;&lt;Kind Name="Machine.Specifications_Field" /&gt;&lt;Kind Name="Machine.Specifications_Constant" /&gt;&lt;Kind Name="Machine.Specifications_Local" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb_aaBb"&gt;&lt;ExtraRule Prefix="" Suffix="" Style="AaBb_AaBb" /&gt;&lt;/Policy&gt;&lt;/Policy&gt;</s:String></wpf:ResourceDictionary>
19 changes: 12 additions & 7 deletions source/Appccelerate.StateMachine/ActiveStateMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ public class ActiveStateMachine<TState, TEvent> :
private readonly LinkedList<EventInformation<TEvent>> queue;
private readonly TState initialState;

private Task worker;
private CancellationTokenSource stopToken;
private Task? worker;
private CancellationTokenSource stopToken = new CancellationTokenSource();

public ActiveStateMachine(
StateMachine<TState, TEvent> stateMachine,
Expand Down Expand Up @@ -116,7 +116,7 @@ public void Fire(TEvent eventId)
/// </summary>
/// <param name="eventId">The event.</param>
/// <param name="eventArgument">The event argument.</param>
public void Fire(TEvent eventId, object eventArgument)
public void Fire(TEvent eventId, object? eventArgument)
{
lock (this.queue)
{
Expand All @@ -141,7 +141,7 @@ public void FirePriority(TEvent eventId)
/// </summary>
/// <param name="eventId">The event.</param>
/// <param name="eventArgument">The event argument.</param>
public void FirePriority(TEvent eventId, object eventArgument)
public void FirePriority(TEvent eventId, object? eventArgument)
{
lock (this.queue)
{
Expand Down Expand Up @@ -260,12 +260,12 @@ public void Stop()

try
{
this.worker.Wait();
this.worker?.Wait();
}
catch (AggregateException)
{
// in case the task was stopped before it could actually start, it will be canceled.
if (this.worker.IsFaulted)
if (this.worker?.IsFaulted ?? false)
{
throw;
}
Expand Down Expand Up @@ -339,7 +339,12 @@ private void ProcessEventQueue(CancellationToken cancellationToken)
}
}

this.stateMachine.Fire(eventInformation.EventId, eventInformation.EventArgument, this.stateContainer, this.stateContainer, this.stateDefinitions);
this.stateMachine.Fire(
eventInformation.EventId,
eventInformation.EventArgument,
this.stateContainer,
this.stateContainer,
this.stateDefinitions);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

<PropertyGroup>
<TargetFramework>netstandard1.3</TargetFramework>
<LangVersion>8</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>

<PropertyGroup>
Expand Down
15 changes: 9 additions & 6 deletions source/Appccelerate.StateMachine/AsyncActiveStateMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ public class AsyncActiveStateMachine<TState, TEvent> : IAsyncStateMachine<TState
private readonly ConcurrentStack<EventInformation<TEvent>> priorityEvents;
private readonly TState initialState;

private Task worker;
private CancellationTokenSource stopToken;
private Task? worker;
private CancellationTokenSource stopToken = new CancellationTokenSource();
private TaskCompletionSource<bool> workerCompletionSource = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);

public AsyncActiveStateMachine(
Expand Down Expand Up @@ -113,7 +113,7 @@ public async Task Fire(TEvent eventId)
/// <param name="eventId">The event.</param>
/// <param name="eventArgument">The event argument.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task Fire(TEvent eventId, object eventArgument)
public async Task Fire(TEvent eventId, object? eventArgument)
{
this.events.Enqueue(new EventInformation<TEvent>(eventId, eventArgument));

Expand All @@ -140,7 +140,7 @@ public async Task FirePriority(TEvent eventId)
/// <param name="eventId">The event.</param>
/// <param name="eventArgument">The event argument.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task FirePriority(TEvent eventId, object eventArgument)
public async Task FirePriority(TEvent eventId, object? eventArgument)
{
this.priorityEvents.Push(new EventInformation<TEvent>(eventId, eventArgument));

Expand Down Expand Up @@ -332,8 +332,11 @@ public async Task Stop()

try
{
await this.worker
.ConfigureAwait(false);
if (this.worker != null)
{
await this.worker
.ConfigureAwait(false);
}
}
catch (OperationCanceledException)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,14 @@ public ArgumentActionHolder(Action<T> action)

public async Task Execute(object argument)
{
T castArgument = default(T);

if (argument != Missing.Value && argument != null && !(argument is T))
{
throw new ArgumentException(ActionHoldersExceptionMessages.CannotCastArgumentToActionArgument(argument, this.Describe()));
}

if (argument != Missing.Value)
var castArgument = argument switch
{
castArgument = (T)argument;
}
T value => value,
_ => throw new ArgumentException(
ActionHoldersExceptionMessages.CannotCastArgumentToActionArgument(
argument,
this.Describe()))
};

await this.action(castArgument).ConfigureAwait(false);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@ public interface IFactory<TState, TEvent>

IGuardHolder CreateGuardHolder<T>(Func<T, bool> guard);

ITransitionContext<TState, TEvent> CreateTransitionContext(IStateDefinition<TState, TEvent> stateDefinition, Missable<TEvent> eventId, object eventArgument, INotifier<TState, TEvent> notifier);
ITransitionContext<TState, TEvent> CreateTransitionContext(
IStateDefinition<TState, TEvent> stateDefinition,
Missable<TEvent> eventId,
object? eventArgument,
INotifier<TState, TEvent> notifier);

StateMachineInitializer<TState, TEvent> CreateStateMachineInitializer(IStateDefinition<TState, TEvent> initialState, ITransitionContext<TState, TEvent> context);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,14 @@ public ArgumentGuardHolder(Func<T, Task<bool>> guard)
/// <returns>Result of the guard execution.</returns>
public async Task<bool> Execute(object argument)
{
if (argument != null && !(argument is T))
var castArgument = argument switch
{
throw new ArgumentException(GuardHoldersExceptionMessages.CannotCastArgumentToGuardArgument(argument, this.Describe()));
}
T value => value,
_ => throw new ArgumentException(
GuardHoldersExceptionMessages.CannotCastArgumentToGuardArgument(argument, this.Describe()))
};

return await this.guard((T)argument).ConfigureAwait(false);
return await this.guard(castArgument).ConfigureAwait(false);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ public virtual IGuardHolder CreateGuardHolder<T>(Func<T, Task<bool>> guard)
return new ArgumentGuardHolder<T>(guard);
}

public virtual ITransitionContext<TState, TEvent> CreateTransitionContext(IStateDefinition<TState, TEvent> state, Missable<TEvent> eventId, object eventArgument, INotifier<TState, TEvent> notifier)
public virtual ITransitionContext<TState, TEvent> CreateTransitionContext(IStateDefinition<TState, TEvent> state, Missable<TEvent> eventId, object? eventArgument, INotifier<TState, TEvent> notifier)
{
return new TransitionContext<TState, TEvent>(state, eventId, eventArgument, notifier);
}
Expand Down
7 changes: 4 additions & 3 deletions source/Appccelerate.StateMachine/AsyncMachine/StateMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ namespace Appccelerate.StateMachine.AsyncMachine
using Events;
using Infrastructure;
using States;
using Transitions;

/// <summary>
/// Base implementation of a state machine.
Expand Down Expand Up @@ -119,7 +120,7 @@ await stateContainer.ForEach(extension => extension.EnteredInitialState(stateMac
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task Fire(
TEvent eventId,
object eventArgument,
object? eventArgument,
StateContainer<TState, TEvent> stateContainer,
IStateMachineInformation<TState, TEvent> stateMachineInformation,
IStateDefinitionDictionary<TState, TEvent> stateDefinitions)
Expand All @@ -134,13 +135,13 @@ await stateContainer.ForEach(extension => extension.FiringEvent(stateMachineInfo
var result = await this.stateLogic.Fire(currentState, context, stateContainer)
.ConfigureAwait(false);

if (!result.Fired)
if (!(result is FiredTransitionResult<TState> firedTransitionResult))
{
this.OnTransitionDeclined(context);
return;
}

var newState = stateDefinitions[result.NewState];
var newState = stateDefinitions[firedTransitionResult.NewState];
await SwitchStateTo(newState, stateContainer, stateMachineInformation)
.ConfigureAwait(false);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public async Task<ITransitionResult<TState>> Fire(
{
Guard.AgainstNullArgument("context", context);

var result = TransitionResult<TState>.NotFired;
ITransitionResult<TState> result = new NotFiredTransitionResult<TState>();

if (stateDefinition.Transitions.TryGetValue(context.EventId.Value, out var transitionsForEvent))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,23 +20,25 @@ namespace Appccelerate.StateMachine.AsyncMachine.Transitions
{
using System;

public class TransitionResult<TState>
/// <summary>
/// Represents a fired transition.
/// </summary>
/// <typeparam name="TState">ype of the states.</typeparam>
public class FiredTransitionResult<TState>
: ITransitionResult<TState>
where TState : IComparable
{
public static readonly ITransitionResult<TState> NotFired = new TransitionResult<TState>(false, default(TState));

public TransitionResult(bool fired, TState newState)
public FiredTransitionResult(
TState newState)
{
this.Fired = fired;
this.NewState = newState;
}

/// <summary>
/// Gets a value indicating whether this <see cref="ITransitionResult{TState}"/> is fired.
/// </summary>
/// <value><c>true</c> if fired; otherwise, <c>false</c>.</value>
public bool Fired { get; }
public bool Fired => true;

/// <summary>
/// Gets the new state the state machine is in.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,5 @@ public interface ITransitionResult<TState>
/// </summary>
/// <value><c>true</c> if fired; otherwise, <c>false</c>.</value>
bool Fired { get; }

/// <summary>
/// Gets the new state.
/// </summary>
/// <value>The new state.</value>
TState NewState { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
namespace Appccelerate.StateMachine.AsyncMachine.Transitions
{
using System;

/// <summary>
/// Represents a not fired transition - there was no transition found for an event in the current state or a super state.
/// </summary>
/// <typeparam name="TState">ype of the states.</typeparam>
public class NotFiredTransitionResult<TState> : ITransitionResult<TState>
where TState : IComparable
{
/// <summary>
/// Gets a value indicating whether this <see cref="ITransitionResult{TState}"/> is fired.
/// </summary>
/// <value><c>true</c> if fired; otherwise, <c>false</c>.</value>
public bool Fired => false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ await this.extensionHost
context))
.ConfigureAwait(false);

return TransitionResult<TState>.NotFired;
return new NotFiredTransitionResult<TState>();
}

context.OnTransitionBegin();
Expand Down Expand Up @@ -98,7 +98,7 @@ await this.extensionHost
context))
.ConfigureAwait(false);

return new TransitionResult<TState>(true, newState);
return new FiredTransitionResult<TState>(newState);
}

private static void HandleException(Exception exception, ITransitionContext<TState, TEvent> context)
Expand Down
4 changes: 2 additions & 2 deletions source/Appccelerate.StateMachine/AsyncPassiveStateMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public async Task Fire(TEvent eventId)
/// <param name="eventId">The event.</param>
/// <param name="eventArgument">The event argument.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task Fire(TEvent eventId, object eventArgument)
public async Task Fire(TEvent eventId, object? eventArgument)
{
this.events.Enqueue(new EventInformation<TEvent>(eventId, eventArgument));

Expand All @@ -143,7 +143,7 @@ public async Task FirePriority(TEvent eventId)
/// <param name="eventId">The event.</param>
/// <param name="eventArgument">The event argument.</param>
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
public async Task FirePriority(TEvent eventId, object eventArgument)
public async Task FirePriority(TEvent eventId, object? eventArgument)
{
this.priorityEvents.Push(new EventInformation<TEvent>(eventId, eventArgument));

Expand Down
Loading