From dd068c41ea06addafe4098675b10d9cf01b70711 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Sun, 3 Mar 2019 07:30:05 +0100 Subject: [PATCH 1/8] Added tests showing If is not working for exceptions --- src/Automatonymous.Tests/Exception_Specs.cs | 23 ++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/Automatonymous.Tests/Exception_Specs.cs b/src/Automatonymous.Tests/Exception_Specs.cs index a89b0ea..a5d766c 100644 --- a/src/Automatonymous.Tests/Exception_Specs.cs +++ b/src/Automatonymous.Tests/Exception_Specs.cs @@ -48,6 +48,8 @@ public Instance() public State CurrentState { get; set; } public bool ShouldNotBeCalled { get; set; } + + public bool CalledThenClause { get; set; } } @@ -62,6 +64,9 @@ public InstanceStateMachine() .Then(_ => { throw new ApplicationException("Boom!"); }) .Then(context => context.Instance.NotCalled = false) .Catch(ex => ex + .If(context => true, b => b + .Then(context => context.Instance.CalledThenClause = true) + ) .Then(context => { context.Instance.ExceptionMessage = context.Exception.Message; @@ -122,6 +127,12 @@ public void Should_not_have_called_the_second_action() { Assert.IsTrue(_instance.NotCalled); } + + [Test] + public void Should_have_called_the_first_if_block() + { + Assert.IsTrue(_instance.CalledThenClause); + } } [TestFixture] @@ -291,6 +302,8 @@ public Instance() public Type ExceptionType { get; set; } public string ExceptionMessage { get; set; } public State CurrentState { get; set; } + + public bool CalledThenClause { get; set; } } @@ -312,13 +325,15 @@ public InstanceStateMachine() .Then(_ => { throw new ApplicationException("Boom!"); }) .Then(context => context.Instance.NotCalled = false) .Catch(ex => ex + .If(context => true, b => b + .Then(context => context.Instance.CalledThenClause = true) + ) .Then(context => { context.Instance.ExceptionMessage = context.Exception.Message; context.Instance.ExceptionType = context.Exception.GetType(); }) .TransitionTo(Failed))); - } public State Failed { get; private set; } @@ -356,5 +371,11 @@ public void Should_not_have_called_the_second_action() { Assert.IsTrue(_instance.NotCalled); } + + [Test] + public void Should_have_called_the_first_if_block() + { + Assert.IsTrue(_instance.CalledThenClause); + } } } \ No newline at end of file From ab420d8258dc98013d0c187f62696d46a317b103 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Sun, 3 Mar 2019 07:41:51 +0100 Subject: [PATCH 2/8] Fixed If not working for exceptions --- .../Activities/ConditionExceptionActivity.cs | 143 ++++++++++++++++++ .../Binders/CatchExceptionActivityBinder.cs | 4 +- .../ConditionalExceptionActivityBinder.cs | 123 +++++++++++++++ 3 files changed, 268 insertions(+), 2 deletions(-) create mode 100644 src/Automatonymous/Activities/ConditionExceptionActivity.cs create mode 100644 src/Automatonymous/Binders/ConditionalExceptionActivityBinder.cs diff --git a/src/Automatonymous/Activities/ConditionExceptionActivity.cs b/src/Automatonymous/Activities/ConditionExceptionActivity.cs new file mode 100644 index 0000000..019cd65 --- /dev/null +++ b/src/Automatonymous/Activities/ConditionExceptionActivity.cs @@ -0,0 +1,143 @@ +// 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.Activities +{ + using System; + using System.Threading.Tasks; + using GreenPipes; + + + public class ConditionExceptionActivity : + Activity + where TInstance : class + where TConditionException : Exception + { + readonly Behavior _behavior; + readonly StateMachineExceptionCondition _condition; + + public ConditionExceptionActivity(StateMachineExceptionCondition condition, Behavior behavior) + { + _condition = condition; + _behavior = behavior; + } + + void IProbeSite.Probe(ProbeContext context) + { + var scope = context.CreateScope("condition"); + + _behavior.Probe(scope); + } + + void Visitable.Accept(StateMachineVisitor visitor) + { + visitor.Visit(this, x => _behavior.Accept(visitor)); + } + + Task Activity.Execute(BehaviorContext context, Behavior next) + { + return next.Execute(context); + } + + Task Activity.Execute(BehaviorContext context, Behavior next) + { + return next.Execute(context); + } + + async Task Activity.Faulted(BehaviorExceptionContext context, Behavior next) + { + var behaviorContext = context as BehaviorExceptionContext; + if (behaviorContext != null) + { + if (_condition(behaviorContext)) + { + await _behavior.Faulted(context).ConfigureAwait(false); + } + } + + await next.Faulted(context).ConfigureAwait(false); + } + + async Task Activity.Faulted(BehaviorExceptionContext context, + Behavior next) + { + var behaviorContext = context as BehaviorExceptionContext; + if (behaviorContext != null) + { + if (_condition(behaviorContext)) + { + await _behavior.Faulted(context).ConfigureAwait(false); + } + } + + await next.Faulted(context).ConfigureAwait(false); + } + } + + + public class ConditionExceptionActivity : + Activity + where TInstance : class + where TConditionException : Exception + { + readonly Behavior _behavior; + readonly StateMachineExceptionCondition _condition; + + public ConditionExceptionActivity(StateMachineExceptionCondition condition, Behavior behavior) + { + _condition = condition; + _behavior = behavior; + } + + void IProbeSite.Probe(ProbeContext context) + { + var scope = context.CreateScope("condition"); + + _behavior.Probe(scope); + } + + void Visitable.Accept(StateMachineVisitor visitor) + { + visitor.Visit(this, x => _behavior.Accept(visitor)); + } + + Task Activity.Execute(BehaviorContext context, Behavior next) + { + throw new AutomatonymousException("This activity requires a body with the event, but no body was specified."); + } + + Task Activity.Execute(BehaviorContext context, Behavior next) + { + return next.Execute(context); + } + + Task Activity.Faulted(BehaviorExceptionContext context, Behavior next) + { + throw new AutomatonymousException("This activity requires a body with the event, but no body was specified."); + } + + async Task Activity.Faulted(BehaviorExceptionContext context, + Behavior next) + { + var behaviorContext = context as BehaviorExceptionContext; + if (behaviorContext != null) + { + if (_condition(behaviorContext)) + { + await _behavior.Faulted(context).ConfigureAwait(false); + } + } + + await next.Faulted(context).ConfigureAwait(false); + } + } +} \ No newline at end of file diff --git a/src/Automatonymous/Binders/CatchExceptionActivityBinder.cs b/src/Automatonymous/Binders/CatchExceptionActivityBinder.cs index 17e3cff..2de0860 100644 --- a/src/Automatonymous/Binders/CatchExceptionActivityBinder.cs +++ b/src/Automatonymous/Binders/CatchExceptionActivityBinder.cs @@ -81,7 +81,7 @@ public ExceptionActivityBinder If(StateMachineCondition(_event, condition, binder); + var conditionBinder = new ConditionalExceptionActivityBinder(_event, condition, binder); return new CatchExceptionActivityBinder(_machine, _event, _activities, conditionBinder); } @@ -163,7 +163,7 @@ public ExceptionActivityBinder If(StateMachineCond binder = activityCallback(binder); - var conditionBinder = new ConditionalActivityBinder(_event, condition, binder); + var conditionBinder = new ConditionalExceptionActivityBinder(_event, condition, binder); return new CatchExceptionActivityBinder(_machine, _event, _activities, conditionBinder); } diff --git a/src/Automatonymous/Binders/ConditionalExceptionActivityBinder.cs b/src/Automatonymous/Binders/ConditionalExceptionActivityBinder.cs new file mode 100644 index 0000000..643e9a5 --- /dev/null +++ b/src/Automatonymous/Binders/ConditionalExceptionActivityBinder.cs @@ -0,0 +1,123 @@ +// 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.Binders +{ + using System; + using Activities; + using Behaviors; + + + public class ConditionalExceptionActivityBinder : + ActivityBinder + where TInstance : class + where TException : Exception + { + readonly EventActivities _activities; + readonly StateMachineCondition _condition; + readonly Event _event; + + public ConditionalExceptionActivityBinder(Event @event, StateMachineCondition condition, EventActivities activities) + { + _activities = activities; + _condition = condition; + _event = @event; + } + + public bool IsStateTransitionEvent(State state) + { + return Equals(_event, state.Enter) || Equals(_event, state.BeforeEnter) + || Equals(_event, state.AfterLeave) || Equals(_event, state.Leave); + } + + public void Bind(State state) + { + var catchBuilder = new CatchBehaviorBuilder(); + + foreach (var activity in _activities.GetStateActivityBinders()) + { + activity.Bind(catchBuilder); + } + + var conditionActivity = new ConditionExceptionActivity(_condition, catchBuilder.Behavior); + + state.Bind(_event, conditionActivity); + } + + public void Bind(BehaviorBuilder builder) + { + var catchBuilder = new CatchBehaviorBuilder(); + + foreach (var activity in _activities.GetStateActivityBinders()) + { + activity.Bind(catchBuilder); + } + + var conditionActivity = new ConditionExceptionActivity(_condition, catchBuilder.Behavior); + + builder.Add(conditionActivity); + } + } + + + public class ConditionalExceptionActivityBinder : + ActivityBinder + where TInstance : class + where TException : Exception + { + readonly EventActivities _activities; + readonly StateMachineCondition _condition; + readonly Event _event; + + public ConditionalExceptionActivityBinder(Event @event, StateMachineCondition condition, + EventActivities activities) + { + _activities = activities; + _condition = condition; + _event = @event; + } + + public bool IsStateTransitionEvent(State state) + { + return Equals(_event, state.Enter) || Equals(_event, state.BeforeEnter) + || Equals(_event, state.AfterLeave) || Equals(_event, state.Leave); + } + + public void Bind(State state) + { + var catchBuilder = new CatchBehaviorBuilder(); + + foreach (var activity in _activities.GetStateActivityBinders()) + { + activity.Bind(catchBuilder); + } + + var conditionActivity = new ConditionExceptionActivity(_condition, catchBuilder.Behavior); + + state.Bind(_event, conditionActivity); + } + + public void Bind(BehaviorBuilder builder) + { + var catchBuilder = new CatchBehaviorBuilder(); + + foreach (var activity in _activities.GetStateActivityBinders()) + { + activity.Bind(catchBuilder); + } + + var conditionActivity = new ConditionExceptionActivity(_condition, catchBuilder.Behavior); + + builder.Add(conditionActivity); + } + } +} \ No newline at end of file From 397e764561976ae48f6e99aea72210aac2720718 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Sun, 3 Mar 2019 07:44:51 +0100 Subject: [PATCH 3/8] Exposed thrown exception for exceptions If --- .../Binders/CatchExceptionActivityBinder.cs | 8 +-- .../ConditionalExceptionActivityBinder.cs | 8 +-- .../Binders/ExceptionActivityBinder.cs | 4 +- .../StateMachineExceptionCondition.cs | 49 +++++++++++++++++++ 4 files changed, 59 insertions(+), 10 deletions(-) create mode 100644 src/Automatonymous/StateMachineExceptionCondition.cs diff --git a/src/Automatonymous/Binders/CatchExceptionActivityBinder.cs b/src/Automatonymous/Binders/CatchExceptionActivityBinder.cs index 2de0860..5b3e0e6 100644 --- a/src/Automatonymous/Binders/CatchExceptionActivityBinder.cs +++ b/src/Automatonymous/Binders/CatchExceptionActivityBinder.cs @@ -74,7 +74,8 @@ public ExceptionActivityBinder Catch( return new CatchExceptionActivityBinder(_machine, _event, _activities, activityBinder); } - public ExceptionActivityBinder If(StateMachineCondition condition, + public ExceptionActivityBinder If( + StateMachineExceptionCondition condition, Func, ExceptionActivityBinder> activityCallback) { ExceptionActivityBinder binder = new CatchExceptionActivityBinder(_machine, _event); @@ -154,9 +155,8 @@ public ExceptionActivityBinder Catch( return new CatchExceptionActivityBinder(_machine, _event, _activities, activityBinder); } - public ExceptionActivityBinder If(StateMachineCondition condition, - Func, ExceptionActivityBinder> - activityCallback) + public ExceptionActivityBinder If(StateMachineExceptionCondition condition, + Func, ExceptionActivityBinder> activityCallback) { ExceptionActivityBinder binder = new CatchExceptionActivityBinder(_machine, _event); diff --git a/src/Automatonymous/Binders/ConditionalExceptionActivityBinder.cs b/src/Automatonymous/Binders/ConditionalExceptionActivityBinder.cs index 643e9a5..3df49fc 100644 --- a/src/Automatonymous/Binders/ConditionalExceptionActivityBinder.cs +++ b/src/Automatonymous/Binders/ConditionalExceptionActivityBinder.cs @@ -23,10 +23,10 @@ public class ConditionalExceptionActivityBinder : where TException : Exception { readonly EventActivities _activities; - readonly StateMachineCondition _condition; + readonly StateMachineExceptionCondition _condition; readonly Event _event; - public ConditionalExceptionActivityBinder(Event @event, StateMachineCondition condition, EventActivities activities) + public ConditionalExceptionActivityBinder(Event @event, StateMachineExceptionCondition condition, EventActivities activities) { _activities = activities; _condition = condition; @@ -75,10 +75,10 @@ public class ConditionalExceptionActivityBinder : where TException : Exception { readonly EventActivities _activities; - readonly StateMachineCondition _condition; + readonly StateMachineExceptionCondition _condition; readonly Event _event; - public ConditionalExceptionActivityBinder(Event @event, StateMachineCondition condition, + public ConditionalExceptionActivityBinder(Event @event, StateMachineExceptionCondition condition, EventActivities activities) { _activities = activities; diff --git a/src/Automatonymous/Binders/ExceptionActivityBinder.cs b/src/Automatonymous/Binders/ExceptionActivityBinder.cs index 9520e8e..a0b7155 100644 --- a/src/Automatonymous/Binders/ExceptionActivityBinder.cs +++ b/src/Automatonymous/Binders/ExceptionActivityBinder.cs @@ -42,7 +42,7 @@ ExceptionActivityBinder Catch( /// /// /// - ExceptionActivityBinder If(StateMachineCondition condition, + ExceptionActivityBinder If(StateMachineExceptionCondition condition, Func, ExceptionActivityBinder> activityCallback); } @@ -76,7 +76,7 @@ ExceptionActivityBinder Catch( /// /// /// - ExceptionActivityBinder If(StateMachineCondition condition, + ExceptionActivityBinder If(StateMachineExceptionCondition condition, Func, ExceptionActivityBinder> activityCallback); } diff --git a/src/Automatonymous/StateMachineExceptionCondition.cs b/src/Automatonymous/StateMachineExceptionCondition.cs new file mode 100644 index 0000000..5344798 --- /dev/null +++ b/src/Automatonymous/StateMachineExceptionCondition.cs @@ -0,0 +1,49 @@ +// Copyright 2011-2019 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. +// Copyright 2011-2019 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 +{ + using System; + + + /// + /// Filters activities based on the conditional statement + /// + /// + /// + /// + /// + public delegate bool StateMachineExceptionCondition(BehaviorExceptionContext context) + where TException : Exception; + + + /// + /// Filters activities based on the conditional statement + /// + /// + /// + /// + public delegate bool StateMachineExceptionCondition(BehaviorExceptionContext context) + where TException : Exception; +} \ No newline at end of file From 35a2c9524a80da3a11da62e2da0dbed407f99745 Mon Sep 17 00:00:00 2001 From: Arnaud Date: Sun, 3 Mar 2019 08:28:09 +0100 Subject: [PATCH 4/8] Added async conditions support for If --- src/Automatonymous.Tests/Condition_Specs.cs | 92 +++++++++++++++++++ src/Automatonymous.Tests/Exception_Specs.cs | 21 +++++ .../Activities/ConditionActivity.cs | 14 +-- .../Activities/ConditionExceptionActivity.cs | 14 +-- .../Binders/CatchExceptionActivityBinder.cs | 26 ++++++ .../Binders/ConditionalActivityBinder.cs | 16 +++- .../ConditionalExceptionActivityBinder.cs | 17 +++- .../Binders/DataEventActivityBinder.cs | 12 +++ .../Binders/EventActivityBinder.cs | 18 ++++ .../Binders/ExceptionActivityBinder.cs | 21 ++++- .../Binders/TriggerEventActivityBinder.cs | 12 +++ .../StateMachineAsyncCondition.cs | 34 +++++++ .../StateMachineAsyncExceptionCondition.cs | 50 ++++++++++ 13 files changed, 326 insertions(+), 21 deletions(-) create mode 100644 src/Automatonymous/StateMachineAsyncCondition.cs create mode 100644 src/Automatonymous/StateMachineAsyncExceptionCondition.cs diff --git a/src/Automatonymous.Tests/Condition_Specs.cs b/src/Automatonymous.Tests/Condition_Specs.cs index f4470ee..6316925 100644 --- a/src/Automatonymous.Tests/Condition_Specs.cs +++ b/src/Automatonymous.Tests/Condition_Specs.cs @@ -104,6 +104,98 @@ class Start } + class StartedExplicitFilter + { + } + } + + [TestFixture] + public class Using_an_async_condition_in_a_state_machine + { + [Test] + public async Task Should_allow_the_condition_to_be_used() + { + await _machine.RaiseEvent(_instance, _machine.Started, new Start {InitializeOnly = true}); + + Assert.That(_instance.CurrentState, Is.EqualTo(_machine.Initialized)); + } + + [Test] + public async Task Should_work() + { + await _machine.RaiseEvent(_instance, _machine.Started, new Start()); + + Assert.That(_instance.CurrentState, Is.EqualTo(_machine.Running)); + } + + [Test] + public async Task Should_allow_if_condition_to_be_evaluated() + { + await _machine.RaiseEvent(_instance, _machine.ExplicitFilterStarted, new StartedExplicitFilter()); + + Assert.That(_instance.CurrentState, Is.Not.EqualTo(_machine.ShouldNotBeHere)); + } + + [SetUp] + public void Specifying_an_event_activity() + { + _instance = new Instance(); + _machine = new InstanceStateMachine(); + } + + Instance _instance; + InstanceStateMachine _machine; + + + class Instance + { + public bool InitializeOnly { get; set; } + public State CurrentState { get; set; } + } + + + class InstanceStateMachine : + AutomatonymousStateMachine + { + public InstanceStateMachine() + { + During(Initial, + When(Started) + .Then(context => context.Instance.InitializeOnly = context.Data.InitializeOnly) + .IfAsync(context => Task.FromResult(context.Data.InitializeOnly), x => x.Then(context => Console.WriteLine("Initializing Only!"))) + .TransitionTo(Initialized)); + + During(Initial, + When(ExplicitFilterStarted, context => true) + .IfAsync(context => Task.FromResult(false), binder => binder + .Then(context => Console.WriteLine("Should not be here!")) + .TransitionTo(ShouldNotBeHere)) + .IfAsync(context => Task.FromResult(true), binder => binder.Then(context => Console.WriteLine("Initializing Only!")))); + + During(Running, + When(Finish) + .Finalize()); + + WhenEnter(Initialized, x => x.IfAsync(context => Task.FromResult(!context.Instance.InitializeOnly), b => b.TransitionTo(Running))); + } + + public State Running { get; private set; } + public State Initialized { get; private set; } + public State ShouldNotBeHere { get; private set; } + + public Event Started { get; private set; } + public Event ExplicitFilterStarted { get; private set; } + + public Event Finish { get; private set; } + } + + + class Start + { + public bool InitializeOnly { get; set; } + } + + class StartedExplicitFilter { } diff --git a/src/Automatonymous.Tests/Exception_Specs.cs b/src/Automatonymous.Tests/Exception_Specs.cs index a5d766c..00ffc45 100644 --- a/src/Automatonymous.Tests/Exception_Specs.cs +++ b/src/Automatonymous.Tests/Exception_Specs.cs @@ -13,6 +13,7 @@ namespace Automatonymous.Tests { using System; + using System.Threading.Tasks; using GreenPipes; using GreenPipes.Introspection; using NUnit.Framework; @@ -50,6 +51,7 @@ public Instance() public bool ShouldNotBeCalled { get; set; } public bool CalledThenClause { get; set; } + public bool CalledSecondThenClause { get; set; } } @@ -67,6 +69,9 @@ public InstanceStateMachine() .If(context => true, b => b .Then(context => context.Instance.CalledThenClause = true) ) + .IfAsync(context => Task.FromResult(true), b => b + .Then(context => context.Instance.CalledSecondThenClause = true) + ) .Then(context => { context.Instance.ExceptionMessage = context.Exception.Message; @@ -133,6 +138,12 @@ public void Should_have_called_the_first_if_block() { Assert.IsTrue(_instance.CalledThenClause); } + + [Test] + public void Should_have_called_the_async_if_block() + { + Assert.IsTrue(_instance.CalledSecondThenClause); + } } [TestFixture] @@ -304,6 +315,7 @@ public Instance() public State CurrentState { get; set; } public bool CalledThenClause { get; set; } + public bool CalledSecondThenClause { get; set; } } @@ -328,6 +340,9 @@ public InstanceStateMachine() .If(context => true, b => b .Then(context => context.Instance.CalledThenClause = true) ) + .IfAsync(context => Task.FromResult(true), b => b + .Then(context => context.Instance.CalledSecondThenClause = true) + ) .Then(context => { context.Instance.ExceptionMessage = context.Exception.Message; @@ -377,5 +392,11 @@ public void Should_have_called_the_first_if_block() { Assert.IsTrue(_instance.CalledThenClause); } + + [Test] + public void Should_have_called_the_async_if_block() + { + Assert.IsTrue(_instance.CalledSecondThenClause); + } } } \ No newline at end of file diff --git a/src/Automatonymous/Activities/ConditionActivity.cs b/src/Automatonymous/Activities/ConditionActivity.cs index 94f6b3a..4edf61c 100644 --- a/src/Automatonymous/Activities/ConditionActivity.cs +++ b/src/Automatonymous/Activities/ConditionActivity.cs @@ -21,9 +21,9 @@ public class ConditionActivity : where TInstance : class { readonly Behavior _behavior; - readonly StateMachineCondition _condition; + readonly StateMachineAsyncCondition _condition; - public ConditionActivity(StateMachineCondition condition, Behavior behavior) + public ConditionActivity(StateMachineAsyncCondition condition, Behavior behavior) { _condition = condition; _behavior = behavior; @@ -43,7 +43,7 @@ void Visitable.Accept(StateMachineVisitor visitor) async Task Activity.Execute(BehaviorContext context, Behavior next) { - if (_condition(context)) + if (await _condition(context).ConfigureAwait(false)) await _behavior.Execute(context).ConfigureAwait(false); await next.Execute(context).ConfigureAwait(false); @@ -51,7 +51,7 @@ async Task Activity.Execute(BehaviorContext context, Behav async Task Activity.Execute(BehaviorContext context, Behavior next) { - if (_condition(context)) + if (await _condition(context).ConfigureAwait(false)) await _behavior.Execute(context).ConfigureAwait(false); await next.Execute(context).ConfigureAwait(false); @@ -75,9 +75,9 @@ public class ConditionActivity : where TInstance : class { readonly Behavior _behavior; - readonly StateMachineCondition _condition; + readonly StateMachineAsyncCondition _condition; - public ConditionActivity(StateMachineCondition condition, Behavior behavior) + public ConditionActivity(StateMachineAsyncCondition condition, Behavior behavior) { _condition = condition; _behavior = behavior; @@ -105,7 +105,7 @@ async Task Activity.Execute(BehaviorContext context, var behaviorContext = context as BehaviorContext; if (behaviorContext != null) { - if (_condition(behaviorContext)) + if (await _condition(behaviorContext).ConfigureAwait(false)) await _behavior.Execute(behaviorContext).ConfigureAwait(false); } diff --git a/src/Automatonymous/Activities/ConditionExceptionActivity.cs b/src/Automatonymous/Activities/ConditionExceptionActivity.cs index 019cd65..92446f1 100644 --- a/src/Automatonymous/Activities/ConditionExceptionActivity.cs +++ b/src/Automatonymous/Activities/ConditionExceptionActivity.cs @@ -23,9 +23,9 @@ public class ConditionExceptionActivity : where TConditionException : Exception { readonly Behavior _behavior; - readonly StateMachineExceptionCondition _condition; + readonly StateMachineAsyncExceptionCondition _condition; - public ConditionExceptionActivity(StateMachineExceptionCondition condition, Behavior behavior) + public ConditionExceptionActivity(StateMachineAsyncExceptionCondition condition, Behavior behavior) { _condition = condition; _behavior = behavior; @@ -58,7 +58,7 @@ async Task Activity.Faulted(BehaviorExceptionContext; if (behaviorContext != null) { - if (_condition(behaviorContext)) + if (await _condition(behaviorContext).ConfigureAwait(false)) { await _behavior.Faulted(context).ConfigureAwait(false); } @@ -73,7 +73,7 @@ async Task Activity.Faulted(BehaviorExceptionContext; if (behaviorContext != null) { - if (_condition(behaviorContext)) + if (await _condition(behaviorContext).ConfigureAwait(false)) { await _behavior.Faulted(context).ConfigureAwait(false); } @@ -90,9 +90,9 @@ public class ConditionExceptionActivity : where TConditionException : Exception { readonly Behavior _behavior; - readonly StateMachineExceptionCondition _condition; + readonly StateMachineAsyncExceptionCondition _condition; - public ConditionExceptionActivity(StateMachineExceptionCondition condition, Behavior behavior) + public ConditionExceptionActivity(StateMachineAsyncExceptionCondition condition, Behavior behavior) { _condition = condition; _behavior = behavior; @@ -131,7 +131,7 @@ async Task Activity.Faulted(BehaviorExceptionContext; if (behaviorContext != null) { - if (_condition(behaviorContext)) + if (await _condition(behaviorContext).ConfigureAwait(false)) { await _behavior.Faulted(context).ConfigureAwait(false); } diff --git a/src/Automatonymous/Binders/CatchExceptionActivityBinder.cs b/src/Automatonymous/Binders/CatchExceptionActivityBinder.cs index 5b3e0e6..bacb7ad 100644 --- a/src/Automatonymous/Binders/CatchExceptionActivityBinder.cs +++ b/src/Automatonymous/Binders/CatchExceptionActivityBinder.cs @@ -86,6 +86,19 @@ public ExceptionActivityBinder If( return new CatchExceptionActivityBinder(_machine, _event, _activities, conditionBinder); } + + public ExceptionActivityBinder IfAsync( + StateMachineAsyncExceptionCondition condition, + Func, ExceptionActivityBinder> activityCallback) + { + ExceptionActivityBinder binder = new CatchExceptionActivityBinder(_machine, _event); + + binder = activityCallback(binder); + + var conditionBinder = new ConditionalExceptionActivityBinder(_event, condition, binder); + + return new CatchExceptionActivityBinder(_machine, _event, _activities, conditionBinder); + } } @@ -167,5 +180,18 @@ public ExceptionActivityBinder If(StateMachineExce return new CatchExceptionActivityBinder(_machine, _event, _activities, conditionBinder); } + + public ExceptionActivityBinder IfAsync(StateMachineAsyncExceptionCondition condition, + Func, ExceptionActivityBinder> activityCallback) + { + ExceptionActivityBinder binder = + new CatchExceptionActivityBinder(_machine, _event); + + binder = activityCallback(binder); + + var conditionBinder = new ConditionalExceptionActivityBinder(_event, condition, binder); + + return new CatchExceptionActivityBinder(_machine, _event, _activities, conditionBinder); + } } } \ No newline at end of file diff --git a/src/Automatonymous/Binders/ConditionalActivityBinder.cs b/src/Automatonymous/Binders/ConditionalActivityBinder.cs index 29e458b..7608ce6 100644 --- a/src/Automatonymous/Binders/ConditionalActivityBinder.cs +++ b/src/Automatonymous/Binders/ConditionalActivityBinder.cs @@ -12,6 +12,7 @@ // specific language governing permissions and limitations under the License. namespace Automatonymous.Binders { + using System.Threading.Tasks; using Activities; using Behaviors; @@ -21,10 +22,15 @@ public class ConditionalActivityBinder : where TInstance : class { readonly EventActivities _activities; - readonly StateMachineCondition _condition; + readonly StateMachineAsyncCondition _condition; readonly Event _event; public ConditionalActivityBinder(Event @event, StateMachineCondition condition, EventActivities activities) + : this(@event, context => Task.FromResult(condition(context)), activities) + { + } + + public ConditionalActivityBinder(Event @event, StateMachineAsyncCondition condition, EventActivities activities) { _activities = activities; _condition = condition; @@ -72,11 +78,17 @@ public class ConditionalActivityBinder : where TInstance : class { readonly EventActivities _activities; - readonly StateMachineCondition _condition; + readonly StateMachineAsyncCondition _condition; readonly Event _event; public ConditionalActivityBinder(Event @event, StateMachineCondition condition, EventActivities activities) + : this(@event, context => Task.FromResult(condition(context)), activities) + { + } + + public ConditionalActivityBinder(Event @event, StateMachineAsyncCondition condition, + EventActivities activities) { _activities = activities; _condition = condition; diff --git a/src/Automatonymous/Binders/ConditionalExceptionActivityBinder.cs b/src/Automatonymous/Binders/ConditionalExceptionActivityBinder.cs index 3df49fc..9a82b35 100644 --- a/src/Automatonymous/Binders/ConditionalExceptionActivityBinder.cs +++ b/src/Automatonymous/Binders/ConditionalExceptionActivityBinder.cs @@ -13,6 +13,7 @@ namespace Automatonymous.Binders { using System; + using System.Threading.Tasks; using Activities; using Behaviors; @@ -23,10 +24,15 @@ public class ConditionalExceptionActivityBinder : where TException : Exception { readonly EventActivities _activities; - readonly StateMachineExceptionCondition _condition; + readonly StateMachineAsyncExceptionCondition _condition; readonly Event _event; public ConditionalExceptionActivityBinder(Event @event, StateMachineExceptionCondition condition, EventActivities activities) + :this(@event, context => Task.FromResult(condition(context)), activities) + { + } + + public ConditionalExceptionActivityBinder(Event @event, StateMachineAsyncExceptionCondition condition, EventActivities activities) { _activities = activities; _condition = condition; @@ -75,10 +81,15 @@ public class ConditionalExceptionActivityBinder : where TException : Exception { readonly EventActivities _activities; - readonly StateMachineExceptionCondition _condition; + readonly StateMachineAsyncExceptionCondition _condition; readonly Event _event; - public ConditionalExceptionActivityBinder(Event @event, StateMachineExceptionCondition condition, + public ConditionalExceptionActivityBinder(Event @event, StateMachineExceptionCondition condition, EventActivities activities) + : this(@event, context => Task.FromResult(condition(context)), activities) + { + } + + public ConditionalExceptionActivityBinder(Event @event, StateMachineAsyncExceptionCondition condition, EventActivities activities) { _activities = activities; diff --git a/src/Automatonymous/Binders/DataEventActivityBinder.cs b/src/Automatonymous/Binders/DataEventActivityBinder.cs index f80cebb..fa299b9 100644 --- a/src/Automatonymous/Binders/DataEventActivityBinder.cs +++ b/src/Automatonymous/Binders/DataEventActivityBinder.cs @@ -94,6 +94,18 @@ EventActivityBinder EventActivityBinder.If(S return new DataEventActivityBinder(_machine, _event, _filter, _activities, conditionBinder); } + EventActivityBinder EventActivityBinder.IfAsync(StateMachineAsyncCondition condition, + Func, EventActivityBinder> activityCallback) + { + EventActivityBinder binder = new DataEventActivityBinder(_machine, _event); + + binder = activityCallback(binder); + + var conditionBinder = new ConditionalActivityBinder(_event, condition, binder); + + return new DataEventActivityBinder(_machine, _event, _filter, _activities, conditionBinder); + } + StateMachine EventActivityBinder.StateMachine => _machine; public IEnumerable> GetStateActivityBinders() diff --git a/src/Automatonymous/Binders/EventActivityBinder.cs b/src/Automatonymous/Binders/EventActivityBinder.cs index 49902c0..56c3f90 100644 --- a/src/Automatonymous/Binders/EventActivityBinder.cs +++ b/src/Automatonymous/Binders/EventActivityBinder.cs @@ -43,6 +43,15 @@ EventActivityBinder Catch( /// EventActivityBinder If(StateMachineCondition condition, Func, EventActivityBinder> activityCallback); + + /// + /// Create a conditional branch of activities for processing + /// + /// + /// + /// + EventActivityBinder IfAsync(StateMachineAsyncCondition condition, + Func, EventActivityBinder> activityCallback); } @@ -76,5 +85,14 @@ EventActivityBinder Catch( /// EventActivityBinder If(StateMachineCondition condition, Func, EventActivityBinder> activityCallback); + + /// + /// Create a conditional branch of activities for processing + /// + /// + /// + /// + EventActivityBinder IfAsync(StateMachineAsyncCondition condition, + Func, EventActivityBinder> activityCallback); } } \ No newline at end of file diff --git a/src/Automatonymous/Binders/ExceptionActivityBinder.cs b/src/Automatonymous/Binders/ExceptionActivityBinder.cs index a0b7155..aaafe35 100644 --- a/src/Automatonymous/Binders/ExceptionActivityBinder.cs +++ b/src/Automatonymous/Binders/ExceptionActivityBinder.cs @@ -44,6 +44,15 @@ ExceptionActivityBinder Catch( /// ExceptionActivityBinder If(StateMachineExceptionCondition condition, Func, ExceptionActivityBinder> activityCallback); + + /// + /// Create a conditional branch of activities for processing + /// + /// + /// + /// + ExceptionActivityBinder IfAsync(StateMachineAsyncExceptionCondition condition, + Func, ExceptionActivityBinder> activityCallback); } @@ -77,7 +86,15 @@ ExceptionActivityBinder Catch( /// /// ExceptionActivityBinder If(StateMachineExceptionCondition condition, - Func, ExceptionActivityBinder> - activityCallback); + Func, ExceptionActivityBinder> activityCallback); + + /// + /// Create a conditional branch of activities for processing + /// + /// + /// + /// + ExceptionActivityBinder IfAsync(StateMachineAsyncExceptionCondition condition, + Func, ExceptionActivityBinder> activityCallback); } } \ No newline at end of file diff --git a/src/Automatonymous/Binders/TriggerEventActivityBinder.cs b/src/Automatonymous/Binders/TriggerEventActivityBinder.cs index b3d7edb..946f984 100644 --- a/src/Automatonymous/Binders/TriggerEventActivityBinder.cs +++ b/src/Automatonymous/Binders/TriggerEventActivityBinder.cs @@ -88,6 +88,18 @@ EventActivityBinder EventActivityBinder.If(StateMachineCon return new TriggerEventActivityBinder(_machine, _event, _filter, _activities, conditionBinder); } + EventActivityBinder EventActivityBinder.IfAsync(StateMachineAsyncCondition condition, + Func, EventActivityBinder> activityCallback) + { + EventActivityBinder binder = new TriggerEventActivityBinder(_machine, _event); + + binder = activityCallback(binder); + + var conditionBinder = new ConditionalActivityBinder(_event, condition, binder); + + return new TriggerEventActivityBinder(_machine, _event, _filter, _activities, conditionBinder); + } + StateMachine EventActivityBinder.StateMachine => _machine; public IEnumerable> GetStateActivityBinders() diff --git a/src/Automatonymous/StateMachineAsyncCondition.cs b/src/Automatonymous/StateMachineAsyncCondition.cs new file mode 100644 index 0000000..4fdb6db --- /dev/null +++ b/src/Automatonymous/StateMachineAsyncCondition.cs @@ -0,0 +1,34 @@ +// Copyright 2011-2019 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 +{ + using System.Threading.Tasks; + + + /// + /// Filters activities based on the async conditional statement + /// + /// + /// + /// + public delegate Task StateMachineAsyncCondition(BehaviorContext context); + + + /// + /// Filters activities based on the async conditional statement + /// + /// + /// + /// + public delegate Task StateMachineAsyncCondition(BehaviorContext context); +} diff --git a/src/Automatonymous/StateMachineAsyncExceptionCondition.cs b/src/Automatonymous/StateMachineAsyncExceptionCondition.cs new file mode 100644 index 0000000..fb4bfe1 --- /dev/null +++ b/src/Automatonymous/StateMachineAsyncExceptionCondition.cs @@ -0,0 +1,50 @@ +// Copyright 2011-2019 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. +// Copyright 2011-2019 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 +{ + using System; + using System.Threading.Tasks; + + + /// + /// Filters activities based on the conditional statement + /// + /// + /// + /// + /// + public delegate Task StateMachineAsyncExceptionCondition(BehaviorExceptionContext context) + where TException : Exception; + + + /// + /// Filters activities based on the conditional statement + /// + /// + /// + /// + public delegate Task StateMachineAsyncExceptionCondition(BehaviorExceptionContext context) + where TException : Exception; +} \ No newline at end of file From c6470625f7af2d179f1dbfa135bca5be8158b77f Mon Sep 17 00:00:00 2001 From: Arnaud Date: Sun, 3 Mar 2019 10:32:34 +0100 Subject: [PATCH 5/8] Added else conditions for If/IfAsync (IfElse/IfElseAsync) --- src/Automatonymous.Tests/Condition_Specs.cs | 42 +++++++-- src/Automatonymous.Tests/Exception_Specs.cs | 81 ++++++++++++++++- .../Activities/ConditionActivity.cs | 40 ++++++--- .../Activities/ConditionExceptionActivity.cs | 48 +++++++--- .../Binders/CatchExceptionActivityBinder.cs | 76 +++++++++++----- .../Binders/ConditionalActivityBinder.cs | 88 +++++++++++-------- .../ConditionalExceptionActivityBinder.cs | 80 ++++++++++------- .../Binders/DataEventActivityBinder.cs | 42 ++++++--- .../Binders/EventActivityBinder.cs | 44 ++++++++++ .../Binders/ExceptionActivityBinder.cs | 44 ++++++++++ .../Binders/TriggerEventActivityBinder.cs | 41 ++++++--- 11 files changed, 477 insertions(+), 149 deletions(-) diff --git a/src/Automatonymous.Tests/Condition_Specs.cs b/src/Automatonymous.Tests/Condition_Specs.cs index 6316925..e44f339 100644 --- a/src/Automatonymous.Tests/Condition_Specs.cs +++ b/src/Automatonymous.Tests/Condition_Specs.cs @@ -44,6 +44,15 @@ public async Task Should_allow_if_condition_to_be_evaluated() Assert.That(_instance.CurrentState, Is.Not.EqualTo(_machine.ShouldNotBeHere)); } + [Test] + public async Task Should_evaluate_else_statement_when_if_condition__is_false() + { + await _machine.RaiseEvent(_instance, _machine.ExplicitFilterStarted, new StartedExplicitFilter()); + + Assert.That(_instance.ShouldBeCalled, Is.True); + } + + [SetUp] public void Specifying_an_event_activity() { @@ -59,6 +68,8 @@ class Instance { public bool InitializeOnly { get; set; } public State CurrentState { get; set; } + + public bool ShouldBeCalled { get; set; } } @@ -75,10 +86,13 @@ public InstanceStateMachine() During(Initial, When(ExplicitFilterStarted, context => true) - .If(context => false, binder => binder - .Then(context => Console.WriteLine("Should not be here!")) - .TransitionTo(ShouldNotBeHere)) - .If(context => true, binder => binder.Then(context => Console.WriteLine("Initializing Only!")))); + .IfElse(context => false, + binder => binder + .Then(context => Console.WriteLine("Should not be here!")) + .TransitionTo(ShouldNotBeHere), + binder => binder + .Then(context => context.Instance.ShouldBeCalled = true) + .Then(context => Console.WriteLine("Initializing Only!")))); During(Running, When(Finish) @@ -136,6 +150,14 @@ public async Task Should_allow_if_condition_to_be_evaluated() Assert.That(_instance.CurrentState, Is.Not.EqualTo(_machine.ShouldNotBeHere)); } + [Test] + public async Task Should_evaluate_else_statement_when_if_condition__is_false() + { + await _machine.RaiseEvent(_instance, _machine.ExplicitFilterStarted, new StartedExplicitFilter()); + + Assert.That(_instance.ShouldBeCalled, Is.True); + } + [SetUp] public void Specifying_an_event_activity() { @@ -151,6 +173,7 @@ class Instance { public bool InitializeOnly { get; set; } public State CurrentState { get; set; } + public bool ShouldBeCalled { get; set; } } @@ -167,10 +190,13 @@ public InstanceStateMachine() During(Initial, When(ExplicitFilterStarted, context => true) - .IfAsync(context => Task.FromResult(false), binder => binder - .Then(context => Console.WriteLine("Should not be here!")) - .TransitionTo(ShouldNotBeHere)) - .IfAsync(context => Task.FromResult(true), binder => binder.Then(context => Console.WriteLine("Initializing Only!")))); + .IfElseAsync(context => Task.FromResult(false), + binder => binder + .Then(context => Console.WriteLine("Should not be here!")) + .TransitionTo(ShouldNotBeHere), + binder => binder + .Then(context => context.Instance.ShouldBeCalled = true) + .Then(context => Console.WriteLine("Initializing Only!")))); During(Running, When(Finish) diff --git a/src/Automatonymous.Tests/Exception_Specs.cs b/src/Automatonymous.Tests/Exception_Specs.cs index 00ffc45..e710de5 100644 --- a/src/Automatonymous.Tests/Exception_Specs.cs +++ b/src/Automatonymous.Tests/Exception_Specs.cs @@ -51,7 +51,12 @@ public Instance() public bool ShouldNotBeCalled { get; set; } public bool CalledThenClause { get; set; } - public bool CalledSecondThenClause { get; set; } + public bool CalledThenClauseAsync { get; set; } + + public bool ThenShouldNotBeCalled { get; set; } + public bool ElseShouldBeCalled { get; set; } + public bool ThenAsyncShouldNotBeCalled { get; set; } + public bool ElseAsyncShouldBeCalled { get; set; } } @@ -70,7 +75,15 @@ public InstanceStateMachine() .Then(context => context.Instance.CalledThenClause = true) ) .IfAsync(context => Task.FromResult(true), b => b - .Then(context => context.Instance.CalledSecondThenClause = true) + .Then(context => context.Instance.CalledThenClauseAsync = true) + ) + .IfElse(context => false, + b => b.Then(context => context.Instance.ThenShouldNotBeCalled = true), + b => b.Then(context => context.Instance.ElseShouldBeCalled = true) + ) + .IfElseAsync(context => Task.FromResult(false), + b => b.Then(context => context.Instance.ThenAsyncShouldNotBeCalled = true), + b => b.Then(context => context.Instance.ElseAsyncShouldBeCalled = true) ) .Then(context => { @@ -142,7 +155,31 @@ public void Should_have_called_the_first_if_block() [Test] public void Should_have_called_the_async_if_block() { - Assert.IsTrue(_instance.CalledSecondThenClause); + Assert.IsTrue(_instance.CalledThenClauseAsync); + } + + [Test] + public void Should_not_have_called_the_false_condition_then_block() + { + Assert.IsFalse(_instance.ThenShouldNotBeCalled); + } + + [Test] + public void Should_not_have_called_the_false_async_condition_then_block() + { + Assert.IsFalse(_instance.ThenAsyncShouldNotBeCalled); + } + + [Test] + public void Should_have_called_the_false_condition_else_block() + { + Assert.IsTrue(_instance.ElseShouldBeCalled); + } + + [Test] + public void Should_have_called_the_false_async_condition_else_block() + { + Assert.IsTrue(_instance.ElseAsyncShouldBeCalled); } } @@ -316,6 +353,11 @@ public Instance() public bool CalledThenClause { get; set; } public bool CalledSecondThenClause { get; set; } + + public bool ThenShouldNotBeCalled { get; set; } + public bool ElseShouldBeCalled { get; set; } + public bool ThenAsyncShouldNotBeCalled { get; set; } + public bool ElseAsyncShouldBeCalled { get; set; } } @@ -343,6 +385,14 @@ public InstanceStateMachine() .IfAsync(context => Task.FromResult(true), b => b .Then(context => context.Instance.CalledSecondThenClause = true) ) + .IfElse(context => false, + b => b.Then(context => context.Instance.ThenShouldNotBeCalled = true), + b => b.Then(context => context.Instance.ElseShouldBeCalled = true) + ) + .IfElseAsync(context => Task.FromResult(false), + b => b.Then(context => context.Instance.ThenAsyncShouldNotBeCalled = true), + b => b.Then(context => context.Instance.ElseAsyncShouldBeCalled = true) + ) .Then(context => { context.Instance.ExceptionMessage = context.Exception.Message; @@ -398,5 +448,30 @@ public void Should_have_called_the_async_if_block() { Assert.IsTrue(_instance.CalledSecondThenClause); } + + [Test] + public void Should_not_have_called_the_false_condition_then_block() + { + Assert.IsFalse(_instance.ThenShouldNotBeCalled); + } + + [Test] + public void Should_not_have_called_the_false_async_condition_then_block() + { + Assert.IsFalse(_instance.ThenAsyncShouldNotBeCalled); + } + + [Test] + public void Should_have_called_the_false_condition_else_block() + { + Assert.IsTrue(_instance.ElseShouldBeCalled); + } + + [Test] + public void Should_have_called_the_false_async_condition_else_block() + { + Assert.IsTrue(_instance.ElseAsyncShouldBeCalled); + } } + } \ No newline at end of file diff --git a/src/Automatonymous/Activities/ConditionActivity.cs b/src/Automatonymous/Activities/ConditionActivity.cs index 4edf61c..138fefa 100644 --- a/src/Automatonymous/Activities/ConditionActivity.cs +++ b/src/Automatonymous/Activities/ConditionActivity.cs @@ -20,31 +20,37 @@ public class ConditionActivity : Activity where TInstance : class { - readonly Behavior _behavior; + readonly Behavior _thenBehavior; + readonly Behavior _elseBehavior; readonly StateMachineAsyncCondition _condition; - public ConditionActivity(StateMachineAsyncCondition condition, Behavior behavior) + public ConditionActivity(StateMachineAsyncCondition condition, Behavior thenBehavior, Behavior elseBehavior) { _condition = condition; - _behavior = behavior; + _thenBehavior = thenBehavior; + _elseBehavior = elseBehavior; } void IProbeSite.Probe(ProbeContext context) { var scope = context.CreateScope("condition"); - _behavior.Probe(scope); + _thenBehavior.Probe(scope); + _elseBehavior.Probe(scope); } void Visitable.Accept(StateMachineVisitor visitor) { - visitor.Visit(this, x => _behavior.Accept(visitor)); + visitor.Visit(this, x => _thenBehavior.Accept(visitor)); + visitor.Visit(this, x => _elseBehavior.Accept(visitor)); } async Task Activity.Execute(BehaviorContext context, Behavior next) { if (await _condition(context).ConfigureAwait(false)) - await _behavior.Execute(context).ConfigureAwait(false); + await _thenBehavior.Execute(context).ConfigureAwait(false); + else + await _elseBehavior.Execute(context).ConfigureAwait(false); await next.Execute(context).ConfigureAwait(false); } @@ -52,7 +58,9 @@ async Task Activity.Execute(BehaviorContext context, Behav async Task Activity.Execute(BehaviorContext context, Behavior next) { if (await _condition(context).ConfigureAwait(false)) - await _behavior.Execute(context).ConfigureAwait(false); + await _thenBehavior.Execute(context).ConfigureAwait(false); + else + await _elseBehavior.Execute(context).ConfigureAwait(false); await next.Execute(context).ConfigureAwait(false); } @@ -74,25 +82,29 @@ public class ConditionActivity : Activity where TInstance : class { - readonly Behavior _behavior; + readonly Behavior _thenBehavior; + readonly Behavior _elseBehavior; readonly StateMachineAsyncCondition _condition; - public ConditionActivity(StateMachineAsyncCondition condition, Behavior behavior) + public ConditionActivity(StateMachineAsyncCondition condition, Behavior thenBehavior, Behavior elseBehavior) { _condition = condition; - _behavior = behavior; + _thenBehavior = thenBehavior; + _elseBehavior = elseBehavior; } void IProbeSite.Probe(ProbeContext context) { var scope = context.CreateScope("condition"); - _behavior.Probe(scope); + _thenBehavior.Probe(scope); + _elseBehavior.Probe(scope); } void Visitable.Accept(StateMachineVisitor visitor) { - visitor.Visit(this, x => _behavior.Accept(visitor)); + visitor.Visit(this, x => _thenBehavior.Accept(visitor)); + visitor.Visit(this, x => _elseBehavior.Accept(visitor)); } Task Activity.Execute(BehaviorContext context, Behavior next) @@ -106,7 +118,9 @@ async Task Activity.Execute(BehaviorContext context, if (behaviorContext != null) { if (await _condition(behaviorContext).ConfigureAwait(false)) - await _behavior.Execute(behaviorContext).ConfigureAwait(false); + await _thenBehavior.Execute(behaviorContext).ConfigureAwait(false); + else + await _elseBehavior.Execute(behaviorContext).ConfigureAwait(false); } await next.Execute(context).ConfigureAwait(false); diff --git a/src/Automatonymous/Activities/ConditionExceptionActivity.cs b/src/Automatonymous/Activities/ConditionExceptionActivity.cs index 92446f1..7f07494 100644 --- a/src/Automatonymous/Activities/ConditionExceptionActivity.cs +++ b/src/Automatonymous/Activities/ConditionExceptionActivity.cs @@ -22,25 +22,30 @@ public class ConditionExceptionActivity : where TInstance : class where TConditionException : Exception { - readonly Behavior _behavior; + readonly Behavior _thenBehavior; + readonly Behavior _elseBehavior; readonly StateMachineAsyncExceptionCondition _condition; - public ConditionExceptionActivity(StateMachineAsyncExceptionCondition condition, Behavior behavior) + public ConditionExceptionActivity(StateMachineAsyncExceptionCondition condition, + Behavior thenBehavior, Behavior elseBehavior) { _condition = condition; - _behavior = behavior; + _thenBehavior = thenBehavior; + _elseBehavior = elseBehavior; } void IProbeSite.Probe(ProbeContext context) { var scope = context.CreateScope("condition"); - _behavior.Probe(scope); + _thenBehavior.Probe(scope); + _elseBehavior.Probe(scope); } void Visitable.Accept(StateMachineVisitor visitor) { - visitor.Visit(this, x => _behavior.Accept(visitor)); + visitor.Visit(this, x => _thenBehavior.Accept(visitor)); + visitor.Visit(this, x => _elseBehavior.Accept(visitor)); } Task Activity.Execute(BehaviorContext context, Behavior next) @@ -60,7 +65,11 @@ async Task Activity.Faulted(BehaviorExceptionContext.Faulted(BehaviorExceptionContext : where TInstance : class where TConditionException : Exception { - readonly Behavior _behavior; + readonly Behavior _thenBehavior; + readonly Behavior _elseBehavior; readonly StateMachineAsyncExceptionCondition _condition; - public ConditionExceptionActivity(StateMachineAsyncExceptionCondition condition, Behavior behavior) + public ConditionExceptionActivity(StateMachineAsyncExceptionCondition condition, + Behavior thenBehavior, Behavior elseBehavior) { _condition = condition; - _behavior = behavior; + _thenBehavior = thenBehavior; + _elseBehavior = elseBehavior; } void IProbeSite.Probe(ProbeContext context) { var scope = context.CreateScope("condition"); - _behavior.Probe(scope); + _thenBehavior.Probe(scope); + _elseBehavior.Probe(scope); } void Visitable.Accept(StateMachineVisitor visitor) { - visitor.Visit(this, x => _behavior.Accept(visitor)); + visitor.Visit(this, x => _thenBehavior.Accept(visitor)); + visitor.Visit(this, x => _elseBehavior.Accept(visitor)); } Task Activity.Execute(BehaviorContext context, Behavior next) @@ -133,7 +151,11 @@ async Task Activity.Faulted(BehaviorExceptionContext If( StateMachineExceptionCondition condition, Func, ExceptionActivityBinder> activityCallback) { - ExceptionActivityBinder binder = new CatchExceptionActivityBinder(_machine, _event); - - binder = activityCallback(binder); - - var conditionBinder = new ConditionalExceptionActivityBinder(_event, condition, binder); - - return new CatchExceptionActivityBinder(_machine, _event, _activities, conditionBinder); + return IfElse(condition, activityCallback, _ => _); } public ExceptionActivityBinder IfAsync( StateMachineAsyncExceptionCondition condition, Func, ExceptionActivityBinder> activityCallback) { - ExceptionActivityBinder binder = new CatchExceptionActivityBinder(_machine, _event); + return IfElseAsync(condition, activityCallback, _ => _); + } - binder = activityCallback(binder); + public ExceptionActivityBinder IfElse(StateMachineExceptionCondition condition, + Func, ExceptionActivityBinder> thenActivityCallback, + Func, ExceptionActivityBinder> elseActivityCallback) + { + var thenBinder = GetBinder(thenActivityCallback); + var elseBinder = GetBinder(elseActivityCallback); + + var conditionBinder = new ConditionalExceptionActivityBinder(_event, condition, thenBinder, elseBinder); - var conditionBinder = new ConditionalExceptionActivityBinder(_event, condition, binder); + return new CatchExceptionActivityBinder(_machine, _event, _activities, conditionBinder); + } + + public ExceptionActivityBinder IfElseAsync(StateMachineAsyncExceptionCondition condition, + Func, ExceptionActivityBinder> thenActivityCallback, + Func, ExceptionActivityBinder> elseActivityCallback) + { + var thenBinder = GetBinder(thenActivityCallback); + var elseBinder = GetBinder(elseActivityCallback); + + var conditionBinder = new ConditionalExceptionActivityBinder(_event, condition, thenBinder, elseBinder); return new CatchExceptionActivityBinder(_machine, _event, _activities, conditionBinder); } + + private ExceptionActivityBinder GetBinder(Func, ExceptionActivityBinder> callback) + { + ExceptionActivityBinder thenBinder = new CatchExceptionActivityBinder(_machine, _event); + return callback(thenBinder); + } } @@ -171,27 +189,43 @@ public ExceptionActivityBinder Catch( public ExceptionActivityBinder If(StateMachineExceptionCondition condition, Func, ExceptionActivityBinder> activityCallback) { - ExceptionActivityBinder binder = - new CatchExceptionActivityBinder(_machine, _event); + return IfElse(condition, activityCallback, _ => _); + } - binder = activityCallback(binder); + public ExceptionActivityBinder IfAsync(StateMachineAsyncExceptionCondition condition, + Func, ExceptionActivityBinder> activityCallback) + { + return IfElseAsync(condition, activityCallback, _ => _); + } + + public ExceptionActivityBinder IfElse(StateMachineExceptionCondition condition, + Func, ExceptionActivityBinder> thenActivityCallback, + Func, ExceptionActivityBinder> elseActivityCallback) + { + var thenBinder = GetBinder(thenActivityCallback); + var elseBinder = GetBinder(elseActivityCallback); - var conditionBinder = new ConditionalExceptionActivityBinder(_event, condition, binder); + var conditionBinder = new ConditionalExceptionActivityBinder(_event, condition, thenBinder, elseBinder); return new CatchExceptionActivityBinder(_machine, _event, _activities, conditionBinder); } - public ExceptionActivityBinder IfAsync(StateMachineAsyncExceptionCondition condition, - Func, ExceptionActivityBinder> activityCallback) + public ExceptionActivityBinder IfElseAsync(StateMachineAsyncExceptionCondition condition, + Func, ExceptionActivityBinder> thenActivityCallback, + Func, ExceptionActivityBinder> elseActivityCallback) { - ExceptionActivityBinder binder = - new CatchExceptionActivityBinder(_machine, _event); - - binder = activityCallback(binder); + var thenBinder = GetBinder(thenActivityCallback); + var elseBinder = GetBinder(elseActivityCallback); - var conditionBinder = new ConditionalExceptionActivityBinder(_event, condition, binder); + var conditionBinder = new ConditionalExceptionActivityBinder(_event, condition, thenBinder, elseBinder); return new CatchExceptionActivityBinder(_machine, _event, _activities, conditionBinder); } + + private ExceptionActivityBinder GetBinder(Func, ExceptionActivityBinder> callback) + { + ExceptionActivityBinder binder = new CatchExceptionActivityBinder(_machine, _event); + return callback(binder); + } } } \ No newline at end of file diff --git a/src/Automatonymous/Binders/ConditionalActivityBinder.cs b/src/Automatonymous/Binders/ConditionalActivityBinder.cs index 7608ce6..0962c66 100644 --- a/src/Automatonymous/Binders/ConditionalActivityBinder.cs +++ b/src/Automatonymous/Binders/ConditionalActivityBinder.cs @@ -21,18 +21,24 @@ public class ConditionalActivityBinder : ActivityBinder where TInstance : class { - readonly EventActivities _activities; + readonly EventActivities _thenActivities; + readonly EventActivities _elseActivities; readonly StateMachineAsyncCondition _condition; readonly Event _event; - public ConditionalActivityBinder(Event @event, StateMachineCondition condition, EventActivities activities) - : this(@event, context => Task.FromResult(condition(context)), activities) + + + public ConditionalActivityBinder(Event @event, StateMachineCondition condition, + EventActivities thenActivities, EventActivities elseActivities) + : this(@event, context => Task.FromResult(condition(context)), thenActivities, elseActivities) { } - public ConditionalActivityBinder(Event @event, StateMachineAsyncCondition condition, EventActivities activities) + public ConditionalActivityBinder(Event @event, StateMachineAsyncCondition condition, + EventActivities thenActivities, EventActivities elseActivities) { - _activities = activities; + _thenActivities = thenActivities; + _elseActivities = elseActivities; _condition = condition; _event = @event; } @@ -45,30 +51,34 @@ public bool IsStateTransitionEvent(State state) public void Bind(State state) { - var builder = new ActivityBehaviorBuilder(); - - foreach (var activity in _activities.GetStateActivityBinders()) - { - activity.Bind(builder); - } + var thenBehavior = GetBehavior(_thenActivities); + var elseBehavior = GetBehavior(_elseActivities); - var conditionActivity = new ConditionActivity(_condition, builder.Behavior); + var conditionActivity = new ConditionActivity(_condition, thenBehavior, elseBehavior); state.Bind(_event, conditionActivity); } public void Bind(BehaviorBuilder builder) { - var stateBuilder = new ActivityBehaviorBuilder(); + var thenBehavior = GetBehavior(_thenActivities); + var elseBehavior = GetBehavior(_elseActivities); + + var conditionActivity = new ConditionActivity(_condition, thenBehavior, elseBehavior); + + builder.Add(conditionActivity); + } + + private static Behavior GetBehavior(EventActivities activities) + { + var builder = new ActivityBehaviorBuilder(); - foreach (var activity in _activities.GetStateActivityBinders()) + foreach (var activity in activities.GetStateActivityBinders()) { - activity.Bind(stateBuilder); + activity.Bind(builder); } - var conditionActivity = new ConditionActivity(_condition, stateBuilder.Behavior); - - builder.Add(conditionActivity); + return builder.Behavior; } } @@ -77,20 +87,22 @@ public class ConditionalActivityBinder : ActivityBinder where TInstance : class { - readonly EventActivities _activities; + readonly EventActivities _thenActivities; + readonly EventActivities _elseActivities; readonly StateMachineAsyncCondition _condition; readonly Event _event; public ConditionalActivityBinder(Event @event, StateMachineCondition condition, - EventActivities activities) - : this(@event, context => Task.FromResult(condition(context)), activities) + EventActivities thenActivities, EventActivities elseActivities) + : this(@event, context => Task.FromResult(condition(context)), thenActivities, elseActivities) { } public ConditionalActivityBinder(Event @event, StateMachineAsyncCondition condition, - EventActivities activities) + EventActivities thenActivities, EventActivities elseActivities) { - _activities = activities; + _thenActivities = thenActivities; + _elseActivities = elseActivities; _condition = condition; _event = @event; } @@ -103,30 +115,34 @@ public bool IsStateTransitionEvent(State state) public void Bind(State state) { - var builder = new ActivityBehaviorBuilder(); - - foreach (var activity in _activities.GetStateActivityBinders()) - { - activity.Bind(builder); - } + var thenBehavior = GetBehavior(_thenActivities); + var elseBehavior = GetBehavior(_elseActivities); - var conditionActivity = new ConditionActivity(_condition, builder.Behavior); + var conditionActivity = new ConditionActivity(_condition, thenBehavior, elseBehavior); state.Bind(_event, conditionActivity); } public void Bind(BehaviorBuilder builder) { - var stateBuilder = new ActivityBehaviorBuilder(); + var thenBehavior = GetBehavior(_thenActivities); + var elseBehavior = GetBehavior(_elseActivities); + + var conditionActivity = new ConditionActivity(_condition, thenBehavior, elseBehavior); + + builder.Add(conditionActivity); + } + + private static Behavior GetBehavior(EventActivities activities) + { + var builder = new ActivityBehaviorBuilder(); - foreach (var activity in _activities.GetStateActivityBinders()) + foreach (var activity in activities.GetStateActivityBinders()) { - activity.Bind(stateBuilder); + activity.Bind(builder); } - var conditionActivity = new ConditionActivity(_condition, stateBuilder.Behavior); - - builder.Add(conditionActivity); + return builder.Behavior; } } } \ No newline at end of file diff --git a/src/Automatonymous/Binders/ConditionalExceptionActivityBinder.cs b/src/Automatonymous/Binders/ConditionalExceptionActivityBinder.cs index 9a82b35..fe329f6 100644 --- a/src/Automatonymous/Binders/ConditionalExceptionActivityBinder.cs +++ b/src/Automatonymous/Binders/ConditionalExceptionActivityBinder.cs @@ -23,18 +23,22 @@ public class ConditionalExceptionActivityBinder : where TInstance : class where TException : Exception { - readonly EventActivities _activities; + readonly EventActivities _thenActivities; + readonly EventActivities _elseActivities; readonly StateMachineAsyncExceptionCondition _condition; readonly Event _event; - public ConditionalExceptionActivityBinder(Event @event, StateMachineExceptionCondition condition, EventActivities activities) - :this(@event, context => Task.FromResult(condition(context)), activities) + public ConditionalExceptionActivityBinder(Event @event, StateMachineExceptionCondition condition, + EventActivities thenActivities, EventActivities elseActivities) + :this(@event, context => Task.FromResult(condition(context)), thenActivities, elseActivities) { } - public ConditionalExceptionActivityBinder(Event @event, StateMachineAsyncExceptionCondition condition, EventActivities activities) + public ConditionalExceptionActivityBinder(Event @event, StateMachineAsyncExceptionCondition condition, + EventActivities thenActivities, EventActivities elseActivities) { - _activities = activities; + _thenActivities = thenActivities; + _elseActivities = elseActivities; _condition = condition; _event = @event; } @@ -47,30 +51,33 @@ public bool IsStateTransitionEvent(State state) public void Bind(State state) { - var catchBuilder = new CatchBehaviorBuilder(); + var thenBehavior = GetBehavior(_thenActivities); + var elseBehavior = GetBehavior(_elseActivities); - foreach (var activity in _activities.GetStateActivityBinders()) - { - activity.Bind(catchBuilder); - } - - var conditionActivity = new ConditionExceptionActivity(_condition, catchBuilder.Behavior); + var conditionActivity = new ConditionExceptionActivity(_condition, thenBehavior, elseBehavior); state.Bind(_event, conditionActivity); } public void Bind(BehaviorBuilder builder) + { + var thenBehavior = GetBehavior(_thenActivities); + var elseBehavior = GetBehavior(_elseActivities); + + var conditionActivity = new ConditionExceptionActivity(_condition, thenBehavior, elseBehavior); + + builder.Add(conditionActivity); + } + + private Behavior GetBehavior(EventActivities activities) { var catchBuilder = new CatchBehaviorBuilder(); - foreach (var activity in _activities.GetStateActivityBinders()) + foreach (var activity in activities.GetStateActivityBinders()) { activity.Bind(catchBuilder); } - - var conditionActivity = new ConditionExceptionActivity(_condition, catchBuilder.Behavior); - - builder.Add(conditionActivity); + return catchBuilder.Behavior; } } @@ -80,19 +87,22 @@ public class ConditionalExceptionActivityBinder : where TInstance : class where TException : Exception { - readonly EventActivities _activities; + readonly EventActivities _thenActivities; + readonly EventActivities _elseActivities; readonly StateMachineAsyncExceptionCondition _condition; readonly Event _event; - public ConditionalExceptionActivityBinder(Event @event, StateMachineExceptionCondition condition, EventActivities activities) - : this(@event, context => Task.FromResult(condition(context)), activities) + public ConditionalExceptionActivityBinder(Event @event, StateMachineExceptionCondition condition, + EventActivities thenActivities, EventActivities elseActivities) + : this(@event, context => Task.FromResult(condition(context)), thenActivities, elseActivities) { } public ConditionalExceptionActivityBinder(Event @event, StateMachineAsyncExceptionCondition condition, - EventActivities activities) + EventActivities thenActivities, EventActivities elseActivities) { - _activities = activities; + _thenActivities = thenActivities; + _elseActivities = elseActivities; _condition = condition; _event = @event; } @@ -105,30 +115,34 @@ public bool IsStateTransitionEvent(State state) public void Bind(State state) { - var catchBuilder = new CatchBehaviorBuilder(); + var thenBehavior = GetBehavior(_thenActivities); + var elseBehavior = GetBehavior(_elseActivities); - foreach (var activity in _activities.GetStateActivityBinders()) - { - activity.Bind(catchBuilder); - } - - var conditionActivity = new ConditionExceptionActivity(_condition, catchBuilder.Behavior); + var conditionActivity = new ConditionExceptionActivity(_condition, thenBehavior, elseBehavior); state.Bind(_event, conditionActivity); } public void Bind(BehaviorBuilder builder) + { + var thenBehavior = GetBehavior(_thenActivities); + var elseBehavior = GetBehavior(_elseActivities); + + var conditionActivity = new ConditionExceptionActivity(_condition, thenBehavior, elseBehavior); + + builder.Add(conditionActivity); + } + + private Behavior GetBehavior(EventActivities activities) { var catchBuilder = new CatchBehaviorBuilder(); - foreach (var activity in _activities.GetStateActivityBinders()) + foreach (var activity in activities.GetStateActivityBinders()) { activity.Bind(catchBuilder); } - var conditionActivity = new ConditionExceptionActivity(_condition, catchBuilder.Behavior); - - builder.Add(conditionActivity); + return catchBuilder.Behavior; } } } \ No newline at end of file diff --git a/src/Automatonymous/Binders/DataEventActivityBinder.cs b/src/Automatonymous/Binders/DataEventActivityBinder.cs index fa299b9..e947f5e 100644 --- a/src/Automatonymous/Binders/DataEventActivityBinder.cs +++ b/src/Automatonymous/Binders/DataEventActivityBinder.cs @@ -85,27 +85,46 @@ EventActivityBinder EventActivityBinder.Catc EventActivityBinder EventActivityBinder.If(StateMachineCondition condition, Func, EventActivityBinder> activityCallback) { - EventActivityBinder binder = new DataEventActivityBinder(_machine, _event); + return IfElse(condition, activityCallback, _ => _); + } - binder = activityCallback(binder); + EventActivityBinder EventActivityBinder.IfAsync(StateMachineAsyncCondition condition, + Func, EventActivityBinder> activityCallback) + { + return IfElseAsync(condition, activityCallback, _ => _); + } - var conditionBinder = new ConditionalActivityBinder(_event, condition, binder); + public EventActivityBinder IfElse(StateMachineCondition condition, + Func, EventActivityBinder> thenActivityCallback, + Func, EventActivityBinder> elseActivityCallback) + { + var thenBinder = GetBinder(thenActivityCallback); + var elseBinder = GetBinder(elseActivityCallback); + + var conditionBinder = new ConditionalActivityBinder(_event, condition, thenBinder, elseBinder); return new DataEventActivityBinder(_machine, _event, _filter, _activities, conditionBinder); } - EventActivityBinder EventActivityBinder.IfAsync(StateMachineAsyncCondition condition, - Func, EventActivityBinder> activityCallback) + public EventActivityBinder IfElseAsync(StateMachineAsyncCondition condition, + Func, EventActivityBinder> thenActivityCallback, + Func, EventActivityBinder> elseActivityCallback) { - EventActivityBinder binder = new DataEventActivityBinder(_machine, _event); + var thenBinder = GetBinder(thenActivityCallback); + var elseBinder = GetBinder(elseActivityCallback); - binder = activityCallback(binder); - - var conditionBinder = new ConditionalActivityBinder(_event, condition, binder); + var conditionBinder = new ConditionalActivityBinder(_event, condition, thenBinder, elseBinder); return new DataEventActivityBinder(_machine, _event, _filter, _activities, conditionBinder); } + private EventActivityBinder GetBinder(Func, EventActivityBinder> activityCallback) + { + EventActivityBinder binder = new DataEventActivityBinder(_machine, _event); + + return activityCallback(binder); + } + StateMachine EventActivityBinder.StateMachine => _machine; public IEnumerable> GetStateActivityBinders() @@ -125,9 +144,10 @@ ActivityBinder CreateStateActivityBinder(Activity a ActivityBinder CreateConditionalActivityBinder() { - EventActivityBinder binder = new DataEventActivityBinder(_machine, _event, _activities); + EventActivityBinder thenBinder = new DataEventActivityBinder(_machine, _event, _activities); + EventActivityBinder elseBinder = new DataEventActivityBinder(_machine, _event); - var conditionBinder = new ConditionalActivityBinder(_event, context => _filter(context), binder); + var conditionBinder = new ConditionalActivityBinder(_event, context => _filter(context), thenBinder, elseBinder); return conditionBinder; } diff --git a/src/Automatonymous/Binders/EventActivityBinder.cs b/src/Automatonymous/Binders/EventActivityBinder.cs index 56c3f90..74928ac 100644 --- a/src/Automatonymous/Binders/EventActivityBinder.cs +++ b/src/Automatonymous/Binders/EventActivityBinder.cs @@ -52,6 +52,28 @@ EventActivityBinder If(StateMachineCondition condition, /// EventActivityBinder IfAsync(StateMachineAsyncCondition condition, Func, EventActivityBinder> activityCallback); + + /// + /// Create a conditional branch of activities for processing + /// + /// + /// + /// + /// + EventActivityBinder IfElse(StateMachineCondition condition, + Func, EventActivityBinder> thenActivityCallback, + Func, EventActivityBinder> elseActivityCallback); + + /// + /// Create a conditional branch of activities for processing + /// + /// + /// + /// + /// + EventActivityBinder IfElseAsync(StateMachineAsyncCondition condition, + Func, EventActivityBinder> thenActivityCallback, + Func, EventActivityBinder> elseActivityCallback); } @@ -94,5 +116,27 @@ EventActivityBinder If(StateMachineCondition /// EventActivityBinder IfAsync(StateMachineAsyncCondition condition, Func, EventActivityBinder> activityCallback); + + /// + /// Create a conditional branch of activities for processing + /// + /// + /// + /// + /// + EventActivityBinder IfElse(StateMachineCondition condition, + Func, EventActivityBinder> thenActivityCallback, + Func, EventActivityBinder> elseActivityCallback); + + /// + /// Create a conditional branch of activities for processing + /// + /// + /// + /// + /// + EventActivityBinder IfElseAsync(StateMachineAsyncCondition condition, + Func, EventActivityBinder> thenActivityCallback, + Func, EventActivityBinder> elseActivityCallback); } } \ No newline at end of file diff --git a/src/Automatonymous/Binders/ExceptionActivityBinder.cs b/src/Automatonymous/Binders/ExceptionActivityBinder.cs index aaafe35..7178ac0 100644 --- a/src/Automatonymous/Binders/ExceptionActivityBinder.cs +++ b/src/Automatonymous/Binders/ExceptionActivityBinder.cs @@ -53,6 +53,28 @@ ExceptionActivityBinder If(StateMachineExceptionCondition /// ExceptionActivityBinder IfAsync(StateMachineAsyncExceptionCondition condition, Func, ExceptionActivityBinder> activityCallback); + + /// + /// Create a conditional branch of activities for processing + /// + /// + /// + /// + /// + ExceptionActivityBinder IfElse(StateMachineExceptionCondition condition, + Func, ExceptionActivityBinder> thenActivityCallback, + Func, ExceptionActivityBinder> elseActivityCallback); + + /// + /// Create a conditional branch of activities for processing + /// + /// + /// + /// + /// + ExceptionActivityBinder IfElseAsync(StateMachineAsyncExceptionCondition condition, + Func, ExceptionActivityBinder> thenActivityCallback, + Func, ExceptionActivityBinder> elseActivityCallback); } @@ -96,5 +118,27 @@ ExceptionActivityBinder If(StateMachineExceptionCo /// ExceptionActivityBinder IfAsync(StateMachineAsyncExceptionCondition condition, Func, ExceptionActivityBinder> activityCallback); + + /// + /// Create a conditional branch of activities for processing + /// + /// + /// + /// + /// + ExceptionActivityBinder IfElse(StateMachineExceptionCondition condition, + Func, ExceptionActivityBinder> thenActivityCallback, + Func, ExceptionActivityBinder> elseActivityCallback); + + /// + /// Create a conditional branch of activities for processing + /// + /// + /// + /// + /// + ExceptionActivityBinder IfElseAsync(StateMachineAsyncExceptionCondition condition, + Func, ExceptionActivityBinder> thenActivityCallback, + Func, ExceptionActivityBinder> elseActivityCallback); } } \ No newline at end of file diff --git a/src/Automatonymous/Binders/TriggerEventActivityBinder.cs b/src/Automatonymous/Binders/TriggerEventActivityBinder.cs index 946f984..2b36e9a 100644 --- a/src/Automatonymous/Binders/TriggerEventActivityBinder.cs +++ b/src/Automatonymous/Binders/TriggerEventActivityBinder.cs @@ -79,27 +79,45 @@ EventActivityBinder EventActivityBinder.Catch( EventActivityBinder EventActivityBinder.If(StateMachineCondition condition, Func, EventActivityBinder> activityCallback) { - EventActivityBinder binder = new TriggerEventActivityBinder(_machine, _event); + return IfElse(condition, activityCallback, _ => _); + } - binder = activityCallback(binder); + EventActivityBinder EventActivityBinder.IfAsync(StateMachineAsyncCondition condition, + Func, EventActivityBinder> activityCallback) + { + return IfElseAsync(condition, activityCallback, _ => _); + } + + public EventActivityBinder IfElse(StateMachineCondition condition, + Func, EventActivityBinder> thenActivityCallback, + Func, EventActivityBinder> elseActivityCallback) + { + var thenBinder = GetBinder(thenActivityCallback); + var elseBinder = GetBinder(elseActivityCallback); - var conditionBinder = new ConditionalActivityBinder(_event, condition, binder); + var conditionBinder = new ConditionalActivityBinder(_event, condition, thenBinder, elseBinder); return new TriggerEventActivityBinder(_machine, _event, _filter, _activities, conditionBinder); } - EventActivityBinder EventActivityBinder.IfAsync(StateMachineAsyncCondition condition, - Func, EventActivityBinder> activityCallback) + public EventActivityBinder IfElseAsync(StateMachineAsyncCondition condition, + Func, EventActivityBinder> thenActivityCallback, + Func, EventActivityBinder> elseActivityCallback) { - EventActivityBinder binder = new TriggerEventActivityBinder(_machine, _event); - - binder = activityCallback(binder); + var thenBinder = GetBinder(thenActivityCallback); + var elseBinder = GetBinder(elseActivityCallback); - var conditionBinder = new ConditionalActivityBinder(_event, condition, binder); + var conditionBinder = new ConditionalActivityBinder(_event, condition, thenBinder, elseBinder); return new TriggerEventActivityBinder(_machine, _event, _filter, _activities, conditionBinder); } + private EventActivityBinder GetBinder(Func, EventActivityBinder> activityCallback) + { + EventActivityBinder binder = new TriggerEventActivityBinder(_machine, _event); + return activityCallback(binder); + } + StateMachine EventActivityBinder.StateMachine => _machine; public IEnumerable> GetStateActivityBinders() @@ -112,9 +130,10 @@ public IEnumerable> GetStateActivityBinders() ActivityBinder CreateConditionalActivityBinder() { - EventActivityBinder binder = new TriggerEventActivityBinder(_machine, _event, _activities); + EventActivityBinder thenBinder = new TriggerEventActivityBinder(_machine, _event, _activities); + EventActivityBinder elseBinder = new TriggerEventActivityBinder(_machine, _event); - var conditionBinder = new ConditionalActivityBinder(_event, context => _filter(context), binder); + var conditionBinder = new ConditionalActivityBinder(_event, context => _filter(context), thenBinder, elseBinder); return conditionBinder; } From e7f5731152f7a33eb439e7194ad7bf93347447d6 Mon Sep 17 00:00:00 2001 From: maldworth Date: Sun, 7 Apr 2019 19:43:34 -0400 Subject: [PATCH 6/8] Add Cake Build --- .editorconfig | 24 ++ .gitignore | 9 +- appveyor.yml | 40 +-- build.bat | 5 - build.cake | 110 ++++++++ build.fsx | 108 -------- build.ps1 | 242 ++++++++++++++++++ build.sh | 117 +++++++++ build/parameters.cake | 63 +++++ build/paths.cake | 64 +++++ build/version.cake | 57 +++++ ...onymous.NHibernateIntegration.Tests.csproj | 2 + ...utomatonymous.NHibernateIntegration.csproj | 17 +- .../Automatonymous.Tests.csproj | 6 +- .../Automatonymous.Visualizer.csproj | 15 +- src/Automatonymous/Automatonymous.csproj | 19 +- src/Directory.Build.props | 47 ++++ template.nuspec | 20 -- tools/packages.config | 4 + 19 files changed, 788 insertions(+), 181 deletions(-) create mode 100644 .editorconfig delete mode 100644 build.bat create mode 100644 build.cake delete mode 100644 build.fsx create mode 100644 build.ps1 create mode 100644 build.sh create mode 100644 build/parameters.cake create mode 100644 build/paths.cake create mode 100644 build/version.cake create mode 100644 src/Directory.Build.props delete mode 100644 template.nuspec create mode 100644 tools/packages.config diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4c0cd83 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,24 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + + +[*] +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 4 + +[*.{config,xml,js,json,html,css,sql,csproj,props,yml}] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.sh] +end_of_line = lf + +[*.{cmd, bat}] +end_of_line = crlf \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4776451..e10dac0 100644 --- a/.gitignore +++ b/.gitignore @@ -40,4 +40,11 @@ AppPackages/ *.DS_Store _book -/node_modules \ No newline at end of file +/node_modules + +# Cake +tools/** +!tools/packages.config + +# Artifacts +artifacts/** \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml index 669d9b3..72c1875 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,34 +1,40 @@ +version: 2.1.5.{build} + branches: only: - - master - - develop + - develop + - master + +skip_tags: true + skip_commits: files: - - docs/* + - docs/**/* - '**/*.html' -image: Visual Studio 2017 -build_script: -- cmd: >- - @echo off - - cls - If Not Exist src\.nuget\nuget.exe msbuild src\.nuget\NuGet.targets -Target:RestorePackages +image: + - Visual Studio 2017 - If Not Exist src\packages\FAKE\tools\fake.exe src\.nuget\nuget.exe Install FAKE -OutputDirectory "src\packages" -ExcludeVersion +build: off +test: off - src\packages\FAKE\tools\fake.exe build.fsx %* -test: - assemblies: src/Automatonymous.Tests/bin/Release/Automatonymous.Tests.dll artifacts: -- path: build_artifacts/*.nupkg -- path: build_artifacts/*.xml + - path: artifacts/*.*nupkg + name: Packages + +build_script: + - ps: .\Build.ps1 deploy: +- provider: NuGet + name: Prerelease + api_key: + secure: 0B2TmwiOnNW+CavGbvgr6S8WcuB2A/5enkf/ZrlRW+8Q8AwW5P9T7+B3QDAtYiEi + on: + branch: develop - provider: NuGet name: Release api_key: secure: 0B2TmwiOnNW+CavGbvgr6S8WcuB2A/5enkf/ZrlRW+8Q8AwW5P9T7+B3QDAtYiEi on: branch: master - diff --git a/build.bat b/build.bat deleted file mode 100644 index b40ede5..0000000 --- a/build.bat +++ /dev/null @@ -1,5 +0,0 @@ -@echo off -cls -If Not Exist src\.nuget\nuget.exe msbuild src\.nuget\NuGet.targets -Target:RestorePackages -If Not Exist src\packages\FAKE\tools\fake.exe src\.nuget\nuget.exe Install FAKE -OutputDirectory "src\packages" -ExcludeVersion -src\packages\FAKE\tools\fake.exe build.fsx %* diff --git a/build.cake b/build.cake new file mode 100644 index 0000000..f1f46a0 --- /dev/null +++ b/build.cake @@ -0,0 +1,110 @@ +////////////////////////////////////////////////////////////////////// +// TOOLS +////////////////////////////////////////////////////////////////////// + +// Load other scripts. +#load "./build/parameters.cake" + +////////////////////////////////////////////////////////////////////// +// ARGUMENTS +////////////////////////////////////////////////////////////////////// + +var target = Argument("target", "Default"); + +////////////////////////////////////////////////////////////////////// +// PREPARATION +////////////////////////////////////////////////////////////////////// + +Setup(setupContext => +{ + var buildParams = BuildParameters.GetParameters(setupContext); + buildParams.Initialize(setupContext); + return buildParams; +}); + +////////////////////////////////////////////////////////////////////// +// TASKS +////////////////////////////////////////////////////////////////////// + +Task("Clean") + .Does(data => +{ + CleanDirectories($"./src/**/obj/{data.Configuration}"); + CleanDirectories($"./src/**/bin/{data.Configuration}"); + CleanDirectory(data.Paths.Directories.Artifacts); +}); + +Task("CleanAll") + .Does(data => +{ + CleanDirectories($"./src/**/obj"); + CleanDirectories($"./src/**/bin"); + CleanDirectory(data.Paths.Directories.Artifacts); +}); + +Task("Restore-NuGet") + .Does(data => +{ + DotNetCoreRestore(data.Paths.Directories.Solution.FullPath); +}); + +Task("Build") + .IsDependentOn("Clean") + .IsDependentOn("Restore-NuGet") + .Does(data => +{ + var settings = new DotNetCoreBuildSettings{ + NoRestore = true, + Configuration = data.Configuration, + MSBuildSettings = new DotNetCoreMSBuildSettings().WithProperty("Version", data.Version.Version) + }; + + DotNetCoreBuild(data.Paths.Directories.Solution.FullPath, settings); +}); + +Task("Test") + .IsDependentOn("Build") + .Does(data => +{ + var settings = new DotNetCoreTestSettings + { + NoBuild = true, + Configuration = data.Configuration, + DiagnosticOutput = true + }; + + if(data.IsRunningOnAppVeyor) settings.ArgumentCustomization = args => args.Append($"--test-adapter-path:.").Append("--logger:Appveyor"); + + DotNetCoreTest(data.Paths.Directories.Solution.FullPath, settings); +}); + +Task("Pack") + .IsDependentOn("Build") + // .WithCriteria((context,data) => data.ShouldPublish) + .Does(data => +{ + var settings = new DotNetCorePackSettings{ + NoBuild = true, + OutputDirectory = data.Paths.Directories.Artifacts, + Configuration = data.Configuration, + MSBuildSettings = new DotNetCoreMSBuildSettings().WithProperty("Version", data.Version.Version) + }; + DotNetCorePack(data.Paths.Directories.Solution.FullPath, settings); +}); + +////////////////////////////////////////////////////////////////////// +// TASK TARGETS +////////////////////////////////////////////////////////////////////// + +Task("Default") + .IsDependentOn("Clean") + .IsDependentOn("Restore-NuGet") + .IsDependentOn("Build") + .IsDependentOn("Test") + .IsDependentOn("Pack"); + +////////////////////////////////////////////////////////////////////// +// EXECUTION +////////////////////////////////////////////////////////////////////// + +RunTarget(target); diff --git a/build.fsx b/build.fsx deleted file mode 100644 index ec27e13..0000000 --- a/build.fsx +++ /dev/null @@ -1,108 +0,0 @@ -#r @"src/packages/FAKE/tools/FakeLib.dll" -open System.IO -open Fake -open Fake.AssemblyInfoFile -open Fake.Git.Information -open Fake.SemVerHelper - -let buildArtifactPath = FullName "./build_artifacts" -let packagesPath = FullName "./src/packages" - -let assemblyVersion = "4.1.0.0" -let baseVersion = "4.1.4" - -let envVersion = (environVarOrDefault "APPVEYOR_BUILD_VERSION" (baseVersion + ".0")) -let buildVersion = (envVersion.Substring(0, envVersion.LastIndexOf('.'))) - -let semVersion : SemVerInfo = parse baseVersion - -let Version = semVersion.ToString() - -let branch = (fun _ -> - (environVarOrDefault "APPVEYOR_REPO_BRANCH" (getBranchName ".")) -) - -let FileVersion = (environVarOrDefault "APPVEYOR_BUILD_VERSION" (Version + "." + "0")) - -let informationalVersion = (fun _ -> - let branchName = (branch ".") - let label = if branchName="master" then "" else " (" + branchName + "/" + (getCurrentSHA1 ".").[0..7] + ")" - (FileVersion + label) -) - -let nugetVersion = (fun _ -> - let branchName = (branch ".") - let label = if branchName="master" then "" else "-" + (branchName) - let version = if branchName="master" then Version else FileVersion - (version + label) -) - -let InfoVersion = informationalVersion() -let NuGetVersion = nugetVersion() - -let versionArgs = [ @"/p:Version=""" + NuGetVersion + @""""; @"/p:PackageVersion=""" + NuGetVersion + @""""; @"/p:AssemblyVersion=""" + FileVersion + @""""; @"/p:FileVersion=""" + FileVersion + @""""; @"/p:InformationalVersion=""" + InfoVersion + @"""" ] - -printfn "Using version: %s" Version - -Target "Clean" (fun _ -> - ensureDirectory buildArtifactPath - - CleanDir buildArtifactPath -) - -Target "RestorePackages" (fun _ -> - DotNetCli.Restore (fun p -> { p with Project = "./src/" } ) -) - -Target "Build" (fun _ -> - CreateCSharpAssemblyInfo @".\src\SolutionVersion.cs" - [ Attribute.Title "Automatonymous" - Attribute.Description "Automatonymous, an open source state machine library, usable with MassTransit" - Attribute.Product "Automatonymous" - Attribute.Version assemblyVersion - Attribute.FileVersion FileVersion - Attribute.InformationalVersion InfoVersion - ] - - DotNetCli.Build (fun p-> { p with Project = @".\src\Automatonymous" - Configuration= "Release" - Output = buildArtifactPath - AdditionalArgs = versionArgs }) -) - -type packageInfo = { - Project: string - PackageFile: string - Summary: string - Files: list -} - -Target "Package" (fun _ -> - DotNetCli.Pack (fun p-> { p with - Project = @".\src\Automatonymous" - Configuration= "Release" - OutputPath= buildArtifactPath - AdditionalArgs = versionArgs @ [ @"--include-symbols"; @"--include-source" ] }) - DotNetCli.Pack (fun p-> { p with - Project = @".\src\Automatonymous.NHibernateIntegration" - Configuration= "Release" - OutputPath= buildArtifactPath - AdditionalArgs = versionArgs @ [ @"--include-symbols"; @"--include-source" ] }) - DotNetCli.Pack (fun p-> { p with - Project = @".\src\Automatonymous.Visualizer" - Configuration= "Release" - OutputPath= buildArtifactPath - AdditionalArgs = versionArgs @ [ @"--include-symbols"; @"--include-source" ] }) -) - -Target "Default" (fun _ -> - trace "Build starting..." -) - -"Clean" - ==> "RestorePackages" - ==> "Build" - ==> "Package" - ==> "Default" - -RunTargetOrDefault "Default" \ No newline at end of file diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 0000000..c6c91b2 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,242 @@ +########################################################################## +# This is the Cake bootstrapper script for PowerShell. +# This file was downloaded from https://github.com/cake-build/resources +# Feel free to change this file to fit your needs. +########################################################################## + +<# + +.SYNOPSIS +This is a Powershell script to bootstrap a Cake build. + +.DESCRIPTION +This Powershell script will download NuGet if missing, restore NuGet tools (including Cake) +and execute your Cake build script with the parameters you provide. + +.PARAMETER Script +The build script to execute. +.PARAMETER Target +The build script target to run. +.PARAMETER Configuration +The build configuration to use. +.PARAMETER Verbosity +Specifies the amount of information to be displayed. +.PARAMETER ShowDescription +Shows description about tasks. +.PARAMETER DryRun +Performs a dry run. +.PARAMETER SkipToolPackageRestore +Skips restoring of packages. +.PARAMETER ScriptArgs +Remaining arguments are added here. + +.LINK +https://cakebuild.net + +#> + +[CmdletBinding()] +Param( + [string]$Script = "build.cake", + [string]$Target, + [string]$Configuration, + [ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")] + [string]$Verbosity, + [switch]$ShowDescription, + [Alias("WhatIf", "Noop")] + [switch]$DryRun, + [switch]$SkipToolPackageRestore, + [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)] + [string[]]$ScriptArgs +) + +# Attempt to set highest encryption available for SecurityProtocol. +# PowerShell will not set this by default (until maybe .NET 4.6.x). This +# will typically produce a message for PowerShell v2 (just an info +# message though) +try { + # Set TLS 1.2 (3072), then TLS 1.1 (768), then TLS 1.0 (192), finally SSL 3.0 (48) + # Use integers because the enumeration values for TLS 1.2 and TLS 1.1 won't + # exist in .NET 4.0, even though they are addressable if .NET 4.5+ is + # installed (.NET 4.5 is an in-place upgrade). + [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48 + } catch { + Write-Output 'Unable to set PowerShell to use TLS 1.2 and TLS 1.1 due to old .NET Framework installed. If you see underlying connection closed or trust errors, you may need to upgrade to .NET Framework 4.5+ and PowerShell v3' + } + +[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null +function MD5HashFile([string] $filePath) +{ + if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf)) + { + return $null + } + + [System.IO.Stream] $file = $null; + [System.Security.Cryptography.MD5] $md5 = $null; + try + { + $md5 = [System.Security.Cryptography.MD5]::Create() + $file = [System.IO.File]::OpenRead($filePath) + return [System.BitConverter]::ToString($md5.ComputeHash($file)) + } + finally + { + if ($file -ne $null) + { + $file.Dispose() + } + } +} + +function GetProxyEnabledWebClient +{ + $wc = New-Object System.Net.WebClient + $proxy = [System.Net.WebRequest]::GetSystemWebProxy() + $proxy.Credentials = [System.Net.CredentialCache]::DefaultCredentials + $wc.Proxy = $proxy + return $wc +} + +Write-Host "Preparing to run build script..." + +if(!$PSScriptRoot){ + $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent +} + +$TOOLS_DIR = Join-Path $PSScriptRoot "tools" +$ADDINS_DIR = Join-Path $TOOLS_DIR "Addins" +$MODULES_DIR = Join-Path $TOOLS_DIR "Modules" +$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe" +$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe" +$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" +$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config" +$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum" +$ADDINS_PACKAGES_CONFIG = Join-Path $ADDINS_DIR "packages.config" +$MODULES_PACKAGES_CONFIG = Join-Path $MODULES_DIR "packages.config" + +# Make sure tools folder exists +if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) { + Write-Verbose -Message "Creating tools directory..." + New-Item -Path $TOOLS_DIR -Type directory | out-null +} + +# Make sure that packages.config exist. +if (!(Test-Path $PACKAGES_CONFIG)) { + Write-Verbose -Message "Downloading packages.config..." + try { + $wc = GetProxyEnabledWebClient + $wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) + } catch { + Throw "Could not download packages.config." + } +} + +# Try find NuGet.exe in path if not exists +if (!(Test-Path $NUGET_EXE)) { + Write-Verbose -Message "Trying to find nuget.exe in PATH..." + $existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_ -PathType Container) } + $NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1 + if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) { + Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)." + $NUGET_EXE = $NUGET_EXE_IN_PATH.FullName + } +} + +# Try download NuGet.exe if not exists +if (!(Test-Path $NUGET_EXE)) { + Write-Verbose -Message "Downloading NuGet.exe..." + try { + $wc = GetProxyEnabledWebClient + $wc.DownloadFile($NUGET_URL, $NUGET_EXE) + } catch { + Throw "Could not download NuGet.exe." + } +} + +# Save nuget.exe path to environment to be available to child processed +$ENV:NUGET_EXE = $NUGET_EXE + +# Restore tools from NuGet? +if(-Not $SkipToolPackageRestore.IsPresent) { + Push-Location + Set-Location $TOOLS_DIR + + # Check for changes in packages.config and remove installed tools if true. + [string] $md5Hash = MD5HashFile($PACKAGES_CONFIG) + if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or + ($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) { + Write-Verbose -Message "Missing or changed package.config hash..." + Get-ChildItem -Exclude packages.config,nuget.exe,Cake.Bakery | + Remove-Item -Recurse + } + + Write-Verbose -Message "Restoring tools from NuGet..." + $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occurred while restoring NuGet tools." + } + else + { + $md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII" + } + Write-Verbose -Message ($NuGetOutput | out-string) + + Pop-Location +} + +# Restore addins from NuGet +if (Test-Path $ADDINS_PACKAGES_CONFIG) { + Push-Location + Set-Location $ADDINS_DIR + + Write-Verbose -Message "Restoring addins from NuGet..." + $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$ADDINS_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occurred while restoring NuGet addins." + } + + Write-Verbose -Message ($NuGetOutput | out-string) + + Pop-Location +} + +# Restore modules from NuGet +if (Test-Path $MODULES_PACKAGES_CONFIG) { + Push-Location + Set-Location $MODULES_DIR + + Write-Verbose -Message "Restoring modules from NuGet..." + $NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$MODULES_DIR`"" + + if ($LASTEXITCODE -ne 0) { + Throw "An error occurred while restoring NuGet modules." + } + + Write-Verbose -Message ($NuGetOutput | out-string) + + Pop-Location +} + +# Make sure that Cake has been installed. +if (!(Test-Path $CAKE_EXE)) { + Throw "Could not find Cake.exe at $CAKE_EXE" +} + + + +# Build Cake arguments +$cakeArguments = @("$Script"); +if ($Target) { $cakeArguments += "-target=$Target" } +if ($Configuration) { $cakeArguments += "-configuration=$Configuration" } +if ($Verbosity) { $cakeArguments += "-verbosity=$Verbosity" } +if ($ShowDescription) { $cakeArguments += "-showdescription" } +if ($DryRun) { $cakeArguments += "-dryrun" } +$cakeArguments += $ScriptArgs + +# Start Cake +Write-Host "Running build script..." +&$CAKE_EXE $cakeArguments +exit $LASTEXITCODE diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..b9e1252 --- /dev/null +++ b/build.sh @@ -0,0 +1,117 @@ +#!/usr/bin/env bash + +########################################################################## +# This is the Cake bootstrapper script for Linux and OS X. +# This file was downloaded from https://github.com/cake-build/resources +# Feel free to change this file to fit your needs. +########################################################################## + +# Define directories. +SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +TOOLS_DIR=$SCRIPT_DIR/tools +ADDINS_DIR=$TOOLS_DIR/Addins +MODULES_DIR=$TOOLS_DIR/Modules +NUGET_EXE=$TOOLS_DIR/nuget.exe +CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe +PACKAGES_CONFIG=$TOOLS_DIR/packages.config +PACKAGES_CONFIG_MD5=$TOOLS_DIR/packages.config.md5sum +ADDINS_PACKAGES_CONFIG=$ADDINS_DIR/packages.config +MODULES_PACKAGES_CONFIG=$MODULES_DIR/packages.config + +# Define md5sum or md5 depending on Linux/OSX +MD5_EXE= +if [[ "$(uname -s)" == "Darwin" ]]; then + MD5_EXE="md5 -r" +else + MD5_EXE="md5sum" +fi + +# Define default arguments. +SCRIPT="build.cake" +CAKE_ARGUMENTS=() + +# Parse arguments. +for i in "$@"; do + case $1 in + -s|--script) SCRIPT="$2"; shift ;; + --) shift; CAKE_ARGUMENTS+=("$@"); break ;; + *) CAKE_ARGUMENTS+=("$1") ;; + esac + shift +done + +# Make sure the tools folder exist. +if [ ! -d "$TOOLS_DIR" ]; then + mkdir "$TOOLS_DIR" +fi + +# Make sure that packages.config exist. +if [ ! -f "$TOOLS_DIR/packages.config" ]; then + echo "Downloading packages.config..." + curl -Lsfo "$TOOLS_DIR/packages.config" https://cakebuild.net/download/bootstrapper/packages + if [ $? -ne 0 ]; then + echo "An error occurred while downloading packages.config." + exit 1 + fi +fi + +# Download NuGet if it does not exist. +if [ ! -f "$NUGET_EXE" ]; then + echo "Downloading NuGet..." + curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/latest/nuget.exe + if [ $? -ne 0 ]; then + echo "An error occurred while downloading nuget.exe." + exit 1 + fi +fi + +# Restore tools from NuGet. +pushd "$TOOLS_DIR" >/dev/null +if [ ! -f "$PACKAGES_CONFIG_MD5" ] || [ "$( cat "$PACKAGES_CONFIG_MD5" | sed 's/\r$//' )" != "$( $MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' )" ]; then + find . -type d ! -name . ! -name 'Cake.Bakery' | xargs rm -rf +fi + +mono "$NUGET_EXE" install -ExcludeVersion +if [ $? -ne 0 ]; then + echo "Could not restore NuGet tools." + exit 1 +fi + +$MD5_EXE "$PACKAGES_CONFIG" | awk '{ print $1 }' >| "$PACKAGES_CONFIG_MD5" + +popd >/dev/null + +# Restore addins from NuGet. +if [ -f "$ADDINS_PACKAGES_CONFIG" ]; then + pushd "$ADDINS_DIR" >/dev/null + + mono "$NUGET_EXE" install -ExcludeVersion + if [ $? -ne 0 ]; then + echo "Could not restore NuGet addins." + exit 1 + fi + + popd >/dev/null +fi + +# Restore modules from NuGet. +if [ -f "$MODULES_PACKAGES_CONFIG" ]; then + pushd "$MODULES_DIR" >/dev/null + + mono "$NUGET_EXE" install -ExcludeVersion + if [ $? -ne 0 ]; then + echo "Could not restore NuGet modules." + exit 1 + fi + + popd >/dev/null +fi + +# Make sure that Cake has been installed. +if [ ! -f "$CAKE_EXE" ]; then + echo "Could not find Cake.exe at '$CAKE_EXE'." + exit 1 +fi + +# Start Cake +exec mono "$CAKE_EXE" $SCRIPT "${CAKE_ARGUMENTS[@]}" diff --git a/build/parameters.cake b/build/parameters.cake new file mode 100644 index 0000000..6589f7e --- /dev/null +++ b/build/parameters.cake @@ -0,0 +1,63 @@ +#load "./paths.cake" +#load "./version.cake" + +public class BuildParameters +{ + public string Configuration { get; private set; } + public bool IsLocalBuild { get; private set; } + public bool IsRunningOnUnix { get; private set; } + public bool IsRunningOnWindows { get; private set; } + public bool IsRunningOnAppVeyor { get; private set; } + public bool IsPullRequest { get; private set; } + public bool IsMainMassTransitRepo { get; private set; } + public bool IsMasterBranch { get; private set; } + public bool IsDevelopBranch { get; private set; } + public bool IsTagged { get; private set; } + public BuildPaths Paths { get; private set; } + public BuildVersion Version { get; private set; } + + public bool ShouldPublish + { + get + { + return !IsLocalBuild && IsRunningOnWindows && !IsPullRequest && IsMainMassTransitRepo && (IsMasterBranch + || IsDevelopBranch); + } + } + + public void Initialize(ICakeContext context) + { + Version = BuildVersion.Calculate(context, this); + } + + public static BuildParameters GetParameters(ICakeContext context) + { + if (context == null) + { + throw new ArgumentNullException("context"); + } + + var target = context.Argument("target", "Default"); + var buildSystem = context.BuildSystem(); + + return new BuildParameters { + Configuration = context.Argument("configuration", "Release"), + IsLocalBuild = buildSystem.IsLocalBuild, + IsRunningOnUnix = context.IsRunningOnUnix(), + IsRunningOnWindows = context.IsRunningOnWindows(), + IsRunningOnAppVeyor = buildSystem.AppVeyor.IsRunningOnAppVeyor, + IsPullRequest = buildSystem.AppVeyor.Environment.PullRequest.IsPullRequest, + IsMainMassTransitRepo = StringComparer.OrdinalIgnoreCase.Equals("phatboyg/newid", buildSystem.AppVeyor.Environment.Repository.Name), + IsMasterBranch = StringComparer.OrdinalIgnoreCase.Equals("master", buildSystem.AppVeyor.Environment.Repository.Branch), + IsDevelopBranch = StringComparer.OrdinalIgnoreCase.Equals("develop", buildSystem.AppVeyor.Environment.Repository.Branch), + IsTagged = IsBuildTagged(buildSystem), + Paths = BuildPaths.GetPaths(context) + }; + } + + private static bool IsBuildTagged(BuildSystem buildSystem) + { + return buildSystem.AppVeyor.Environment.Repository.Tag.IsTag + && !string.IsNullOrWhiteSpace(buildSystem.AppVeyor.Environment.Repository.Tag.Name); + } +} diff --git a/build/paths.cake b/build/paths.cake new file mode 100644 index 0000000..82f2bce --- /dev/null +++ b/build/paths.cake @@ -0,0 +1,64 @@ +public class BuildPaths +{ + public BuildDirectories Directories { get; private set; } + + public static BuildPaths GetPaths( + ICakeContext context + ) + { + if (context == null) + { + throw new ArgumentNullException("context"); + } + + + var rootDir = context.MakeAbsolute(context.Directory("./")); + var solutionDir = rootDir.Combine("src"); + var artifactsDir = rootDir.Combine("artifacts"); + + // Directories + var buildDirectories = new BuildDirectories( + artifactsDir, + rootDir, + solutionDir); + + return new BuildPaths + { + Directories = buildDirectories + }; + } +} + +public class BuildFiles +{ + public FilePath VersionProperties { get; private set; } + + public BuildFiles( + FilePath versionProperties + ) + { + VersionProperties = versionProperties; + } +} + +public class BuildDirectories +{ + public DirectoryPath Artifacts { get; } + public DirectoryPath Root { get; } + public DirectoryPath Solution { get; } + public ICollection ToClean { get; } + + public BuildDirectories( + DirectoryPath artifactsDir, + DirectoryPath rootDir, + DirectoryPath solutionDir + ) + { + Artifacts = artifactsDir; + Root = rootDir; + Solution = solutionDir; + ToClean = new[] { + Artifacts + }; + } +} diff --git a/build/version.cake b/build/version.cake new file mode 100644 index 0000000..259865e --- /dev/null +++ b/build/version.cake @@ -0,0 +1,57 @@ +public class BuildVersion +{ + public string Prefix { get; set; } + public string Suffix { get; set; } + public string Metadata { get; set; } + + public string Version => (Prefix + "-" + Suffix).Trim('-') + Metadata; + + public static BuildVersion Calculate(ICakeContext context, BuildParameters buildParameters) + { + if (context == null) + { + throw new ArgumentNullException("context"); + } + + var buildSystem = context.BuildSystem(); + + var prefix = context.EnvironmentVariable("APPVEYOR_BUILD_VERSION") ?? "1.0.0"; + + // Appveyor needs a unique build version, so it's always MAJOR.MINOR.PATCH.{buildnumber}, but + // this doesn't follow SemVer 2.0, we put the build number as metadata + prefix = string.Join(".", prefix.Split('.').Take(3)); + + string suffix = "alpha.9999"; + string metadata = null; + + if(!buildParameters.IsLocalBuild) + { + var buildNumber = buildSystem.AppVeyor.Environment.Build.Number; + var commitHash = buildSystem.AppVeyor.Environment.Repository.Commit.Id; + commitHash = commitHash.Substring(0,Math.Min(commitHash.Length,7)); + + suffix = + buildParameters.IsMasterBranch ? null + : buildParameters.IsDevelopBranch ? "develop" + : "beta"; + + if(suffix != null) + { + suffix += $".{buildNumber}"; + metadata = $"+sha.{commitHash}"; + } + else + { + metadata = $"+build.{buildNumber}.sha.{commitHash}"; + } + } + + + return new BuildVersion + { + Prefix = prefix, + Suffix = suffix, + Metadata = metadata + }; + } +} diff --git a/src/Automatonymous.NHibernateIntegration.Tests/Automatonymous.NHibernateIntegration.Tests.csproj b/src/Automatonymous.NHibernateIntegration.Tests/Automatonymous.NHibernateIntegration.Tests.csproj index d0cc25e..cfc9c84 100644 --- a/src/Automatonymous.NHibernateIntegration.Tests/Automatonymous.NHibernateIntegration.Tests.csproj +++ b/src/Automatonymous.NHibernateIntegration.Tests/Automatonymous.NHibernateIntegration.Tests.csproj @@ -2,7 +2,9 @@ net461 + + diff --git a/src/Automatonymous.NHibernateIntegration/Automatonymous.NHibernateIntegration.csproj b/src/Automatonymous.NHibernateIntegration/Automatonymous.NHibernateIntegration.csproj index 4567152..11e9786 100644 --- a/src/Automatonymous.NHibernateIntegration/Automatonymous.NHibernateIntegration.csproj +++ b/src/Automatonymous.NHibernateIntegration/Automatonymous.NHibernateIntegration.csproj @@ -1,24 +1,23 @@  + + net461 Automatonymous Automatonymous.NHibernateIntegration True ../../Automatonymous.snk - net461 + Automatonymous.NHibernate - bin\$(Configuration)\$(TargetFramework)\Automatonymous.NHibernateIntegration.xml - Automatonymous, an open source state machine library, usable with MassTransit - https://github.com/MassTransit/Automatonymous - https://github.com/MassTransit/Automatonymous/blob/master/LICENSE - https://github.com/MassTransit/Automatonymous - MassTransit - Chris Patterson - 1701;1702;1705;1591;1712 + $(Description) + MassTransit;Automatonymous;NHibernate + $(NoWarn);1701;1702;1705;1591;1712 + + diff --git a/src/Automatonymous.Tests/Automatonymous.Tests.csproj b/src/Automatonymous.Tests/Automatonymous.Tests.csproj index 2b08819..0c60a21 100644 --- a/src/Automatonymous.Tests/Automatonymous.Tests.csproj +++ b/src/Automatonymous.Tests/Automatonymous.Tests.csproj @@ -1,9 +1,10 @@  net452;netcoreapp2.0 - portable + + @@ -11,14 +12,17 @@ + + $(DefineConstants);NETCORE + diff --git a/src/Automatonymous.Visualizer/Automatonymous.Visualizer.csproj b/src/Automatonymous.Visualizer/Automatonymous.Visualizer.csproj index f9fbd35..4d905c8 100644 --- a/src/Automatonymous.Visualizer/Automatonymous.Visualizer.csproj +++ b/src/Automatonymous.Visualizer/Automatonymous.Visualizer.csproj @@ -1,19 +1,16 @@  + net452 + - Automatonymous.Visualizer - bin\$(Configuration)\$(TargetFramework)\Automatonymous.Visualizer.xml - Automatonymous, an open source state machine library, usable with MassTransit - 3.5.11 - https://github.com/MassTransit/Automatonymous - https://github.com/MassTransit/Automatonymous/blob/master/LICENSE - https://github.com/MassTransit/Automatonymous - MassTransit - Chris Patterson + $(Description) + MassTransit;Automatonymous;Visualizer + + diff --git a/src/Automatonymous/Automatonymous.csproj b/src/Automatonymous/Automatonymous.csproj index 8b4962b..e5815e3 100644 --- a/src/Automatonymous/Automatonymous.csproj +++ b/src/Automatonymous/Automatonymous.csproj @@ -1,31 +1,28 @@  + netstandard2.0;net452 - portable True ../../Automatonymous.snk - 7.2 + $(NoWarn);1701;1702;1705;1591;1712 + - Automatonymous - bin\$(Configuration)\$(TargetFramework)\Automatonymous.xml + MassTransit;Automatonymous Automatonymous, an open source state machine library, usable with MassTransit - https://github.com/MassTransit/Automatonymous - https://github.com/MassTransit/Automatonymous/blob/master/LICENSE - https://github.com/MassTransit/Automatonymous - MassTransit state-machine state event - Chris Patterson - Copyright 2012-2018 Chris Patterson - 1701;1702;1705;1591;1712 + + $(DefineConstants);NETCORE + + \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 0000000..fa7fca4 --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,47 @@ + + + + + Automatonymous + Copyright 2007-2019 Chris Patterson + Chris Patterson + latest + true + true + 4 + CS1587,CS1591,CS1998,CS0675,NU5105 + True + portable + + + + full + DEBUG;TRACE;$(DefineConstants) + + + + true + TRACE;$(DefineConstants) + + + + + Apache-2.0 + Automatonymous, an open source state machine library, usable with MassTransit + True + + true + true + snupkg + + + true + + + + $(NoWarn),CS0618 + true + False + + + diff --git a/template.nuspec b/template.nuspec deleted file mode 100644 index 0bf9102..0000000 --- a/template.nuspec +++ /dev/null @@ -1,20 +0,0 @@ - - - - @project@ - @build.number@ - @authors@ - @authors@ - @summary@ - https://github.com/MassTransit/MassTransit/blob/master/LICENSE - https://github.com/MassTransit/MassTransit - http://MassTransit-project.com/wp-content/themes/pandora/slide.1.png - false - @description@ - @releaseNotes@ - MassTransit - @dependencies@ - @references@ - - @files@ - \ No newline at end of file diff --git a/tools/packages.config b/tools/packages.config new file mode 100644 index 0000000..945c3e8 --- /dev/null +++ b/tools/packages.config @@ -0,0 +1,4 @@ + + + + From ab3d656c6b245eebb24a566447695400233edeee Mon Sep 17 00:00:00 2001 From: maldworth Date: Tue, 9 Apr 2019 16:34:15 -0400 Subject: [PATCH 7/8] Update Version --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 72c1875..565b765 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 2.1.5.{build} +version: 2.1.7.{build} branches: only: From 31e77dbdf2cec704e0db1b44e6edcfcb44267130 Mon Sep 17 00:00:00 2001 From: maldworth Date: Tue, 9 Apr 2019 16:38:26 -0400 Subject: [PATCH 8/8] Fix major version to be 4 instead of 2 --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 565b765..49b7396 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 2.1.7.{build} +version: 4.1.7.{build} branches: only: