Skip to content

Commit

Permalink
Fixed MassTransit/MassTransit#717 so that conditions on trigger event…
Browse files Browse the repository at this point in the history
…s is supported
  • Loading branch information
phatboyg committed Dec 8, 2016
1 parent 18cd54b commit 440248d
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 5 deletions.
2 changes: 1 addition & 1 deletion build.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ let packagesPath = FullName "./src/packages"
let keyFile = FullName "./Automatonymous.snk"

let assemblyVersion = "3.5.0.0"
let baseVersion = "3.5.7"
let baseVersion = "3.5.8"

let semVersion : SemVerInfo = parse baseVersion

Expand Down
1 change: 1 addition & 0 deletions src/Automatonymous.Tests/Automatonymous.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
</Compile>
<Compile Include="AsyncActivity_Specs.cs" />
<Compile Include="AutomatonymousStateMachine_Specs.cs" />
<Compile Include="CompositeCondition_Specs.cs" />
<Compile Include="CompositeOrder_Specs.cs" />
<Compile Include="Condition_Specs.cs" />
<Compile Include="Declarative_Specs.cs" />
Expand Down
113 changes: 113 additions & 0 deletions src/Automatonymous.Tests/CompositeCondition_Specs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
// Copyright 2011-2016 Chris Patterson, Dru Sellers
//
// 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 Automatonymous.Tests
{
using System.Threading.Tasks;
using NUnit.Framework;


[TestFixture]
public class When_specifying_a_condition_on_a_composite_event
{
[Test]
public async Task Should_skip_when_not_met()
{
_machine = new TestStateMachine();
_instance = new Instance();
await _machine.RaiseEvent(_instance, _machine.Start);

await _machine.RaiseEvent(_instance, _machine.First);
await _machine.RaiseEvent(_instance, _machine.Second);

Assert.IsFalse(_instance.Called);
Assert.IsFalse(_instance.SecondFirst);
}

[Test]
public async Task Should_call_when_met()
{
_machine = new TestStateMachine();
_instance = new Instance();
await _machine.RaiseEvent(_instance, _machine.Start);

await _machine.RaiseEvent(_instance, _machine.Second);
await _machine.RaiseEvent(_instance, _machine.First);

Assert.IsTrue(_instance.Called);
Assert.IsTrue(_instance.SecondFirst);
}


TestStateMachine _machine;
Instance _instance;


class Instance
{
public CompositeEventStatus CompositeStatus { get; set; }
public bool Called { get; set; }
public bool CalledAfterAll { get; set; }
public State CurrentState { get; set; }
public bool SecondFirst { get; set; }
public bool First { get; set; }
public bool Second { get; set; }
}


sealed class TestStateMachine :
AutomatonymousStateMachine<Instance>
{
public TestStateMachine()
{
Initially(
When(Start)
.TransitionTo(Waiting));

During(Waiting,
When(First)
.Then(context =>
{
context.Instance.First = true;
context.Instance.CalledAfterAll = false;
}),
When(Second)
.Then(context =>
{
context.Instance.SecondFirst = !context.Instance.First;
context.Instance.Second = true;
context.Instance.CalledAfterAll = false;
})
);

CompositeEvent(() => Third, x => x.CompositeStatus, First, Second);

During(Waiting,
When(Third, context => context.Instance.SecondFirst)
.Then(context =>
{
context.Instance.Called = true;
context.Instance.CalledAfterAll = true;
})
.Finalize());
}

public State Waiting { get; private set; }

public Event Start { get; private set; }

public Event First { get; private set; }
public Event Second { get; private set; }
public Event Third { get; private set; }
}
}
}
13 changes: 13 additions & 0 deletions src/Automatonymous/AutomatonymousStateMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,19 @@ protected EventActivityBinder<TInstance> When(Event @event)
return new TriggerEventActivityBinder<TInstance>(this, @event);
}

/// <summary>
/// When the event is fired in this state, and the event data matches the filter expression, execute the chained activities
/// </summary>
/// <typeparam name="TData">The event data type</typeparam>
/// <param name="event">The fired event</param>
/// <param name="filter">The filter applied to the event</param>
/// <returns></returns>
protected EventActivityBinder<TInstance> When(Event @event, StateMachineEventFilter<TInstance> filter)
{
return new TriggerEventActivityBinder<TInstance>(this, @event, filter);
}


/// <summary>
/// When entering the specified state
/// </summary>
Expand Down
33 changes: 29 additions & 4 deletions src/Automatonymous/Binders/TriggerEventActivityBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace Automatonymous.Binders
{
using System;
using System.Collections.Generic;
using System.Linq;


public class TriggerEventActivityBinder<TInstance> :
Expand All @@ -22,6 +23,7 @@ public class TriggerEventActivityBinder<TInstance> :
{
readonly ActivityBinder<TInstance>[] _activities;
readonly Event _event;
readonly StateMachineEventFilter<TInstance> _filter;
readonly StateMachine<TInstance> _machine;

public TriggerEventActivityBinder(StateMachine<TInstance> machine, Event @event, params ActivityBinder<TInstance>[] activities)
Expand All @@ -31,10 +33,21 @@ public TriggerEventActivityBinder(StateMachine<TInstance> machine, Event @event,
_activities = activities ?? new ActivityBinder<TInstance>[0];
}

TriggerEventActivityBinder(StateMachine<TInstance> machine, Event @event, ActivityBinder<TInstance>[] activities,
public TriggerEventActivityBinder(StateMachine<TInstance> machine, Event @event, StateMachineEventFilter<TInstance> filter,
params ActivityBinder<TInstance>[] activities)
{
_event = @event;
_filter = filter;
_machine = machine;
_activities = activities ?? new ActivityBinder<TInstance>[0];
}

TriggerEventActivityBinder(StateMachine<TInstance> machine, Event @event, StateMachineEventFilter<TInstance> filter,
ActivityBinder<TInstance>[] activities,
params ActivityBinder<TInstance>[] appendActivity)
{
_event = @event;
_filter = filter;
_machine = machine;

_activities = new ActivityBinder<TInstance>[activities.Length + appendActivity.Length];
Expand All @@ -48,7 +61,7 @@ EventActivityBinder<TInstance> EventActivityBinder<TInstance>.Add(Activity<TInst
{
ActivityBinder<TInstance> activityBinder = new ExecuteActivityBinder<TInstance>(_event, activity);

return new TriggerEventActivityBinder<TInstance>(_machine, _event, _activities, activityBinder);
return new TriggerEventActivityBinder<TInstance>(_machine, _event, _filter, _activities, activityBinder);
}

EventActivityBinder<TInstance> EventActivityBinder<TInstance>.Catch<T>(
Expand All @@ -60,7 +73,7 @@ EventActivityBinder<TInstance> EventActivityBinder<TInstance>.Add(Activity<TInst

ActivityBinder<TInstance> activityBinder = new CatchActivityBinder<TInstance, T>(_event, binder);

return new TriggerEventActivityBinder<TInstance>(_machine, _event, _activities, activityBinder);
return new TriggerEventActivityBinder<TInstance>(_machine, _event, _filter, _activities, activityBinder);
}

EventActivityBinder<TInstance> EventActivityBinder<TInstance>.If(StateMachineCondition<TInstance> condition,
Expand All @@ -72,14 +85,26 @@ EventActivityBinder<TInstance> EventActivityBinder<TInstance>.Add(Activity<TInst

var conditionBinder = new ConditionalActivityBinder<TInstance>(_event, condition, binder);

return new TriggerEventActivityBinder<TInstance>(_machine, _event, _activities, conditionBinder);
return new TriggerEventActivityBinder<TInstance>(_machine, _event, _filter, _activities, conditionBinder);
}

StateMachine<TInstance> EventActivityBinder<TInstance>.StateMachine => _machine;

public IEnumerable<ActivityBinder<TInstance>> GetStateActivityBinders()
{
if (_filter != null)
return Enumerable.Repeat(CreateConditionalActivityBinder(), 1);

return _activities;
}

ActivityBinder<TInstance> CreateConditionalActivityBinder()
{
EventActivityBinder<TInstance> binder = new TriggerEventActivityBinder<TInstance>(_machine, _event, _activities);

var conditionBinder = new ConditionalActivityBinder<TInstance>(_event, context => _filter(context), binder);

return conditionBinder;
}
}
}
8 changes: 8 additions & 0 deletions src/Automatonymous/StateMachineEventFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@
// specific language governing permissions and limitations under the License.
namespace Automatonymous
{
/// <summary>
/// Delegate for an event filter, which can examine an event and return true if the filter matches the event instance
/// </summary>
/// <typeparam name="TInstance">The state machine instance type</typeparam>
/// <param name="context">The event context</param>
/// <returns>True if the filter matches the data, otherwise false.</returns>
public delegate bool StateMachineEventFilter<in TInstance>(EventContext<TInstance> context);

/// <summary>
/// Delegate for an event filter, which can examine an event and return true if the filter matches the event data
/// </summary>
Expand Down

0 comments on commit 440248d

Please sign in to comment.