コード例 #1
0
        public void should_not_relay_state_argument_on_no_relaying_raise(RaiseWay raiseWay)
        {
            var entered = false;

            // --arrange
            var builder = new Builder <string, int>(OnException);

            builder.DefineState(Initial).AddTransition(Event1, State1);

            builder.DefineState(State1)
            .OnEnter <int>(_ => { })
            .AddTransition(Event2, State2);

            builder.DefineState(State2)
            .OnEnter(() => entered = true);

            var target = builder.Build(Initial);

            target.Raise(Event1, 93);

            // --act
            target.Raise(raiseWay, Event2);

            // --assert
            entered.Should().BeTrue();
        }
コード例 #2
0
        public void should_call_exit_and_enter_on_reentering(RaiseWay raiseWay)
        {
            const string enter = nameof(enter);
            const string exit  = nameof(exit);

            var actual = new List <string>();

            // --arrange
            var builder = new Builder <string, int>(OnException);

            builder.DefineState(Initial).AddTransition(Event1, State1);

            builder
            .DefineState(State1)
            .OnEnter(_ => actual.Add(enter))
            .OnExit(() => actual.Add(exit))
            .AllowReentrancy(Event1);

            var target = builder.Build(Initial);

            target.Raise(raiseWay, Event1);

            // --act
            target.Raise(raiseWay, Event1);

            // --assert
            actual.Should().BeEquivalentTo(enter, exit, enter);
        }
コード例 #3
0
        public void should_relay_argument_from_parent_of_active_state(RaiseWay raiseWay)
        {
            const int expected = 3987;
            var       actual   = expected - 87;

            // --arrange
            var builder = new Builder <string, int>(OnException);

            builder.DefineState(Initial).AddTransition(Event1, Child);

            builder.DefineState(Parent)
            .OnEnter <int>(_ => { });

            builder.DefineState(Child)
            .AsSubstateOf(Parent)
            .AddTransition(Event2, State2);

            builder.DefineState(State2)
            .OnEnter <int>(value => actual = value);

            var target = builder.Build(Initial);

            target.Raise(Event1, expected);

            // --act
            target.Relaying <int>().Raise(raiseWay, Event2);

            // --assert
            actual.Should().Be(expected);
        }
コード例 #4
0
        public void should_relay_state_argument_to_the_next_state(RaiseWay raiseWay)
        {
            const int expected = 3987;
            var       actual   = expected - 3;

            // --arrange
            var builder = new Builder <string, int>(OnException);

            builder.DefineState(Initial).AddTransition(Event1, State1);

            builder.DefineState(State1)
            .OnEnter <int>(_ => { })
            .AddTransition(Event2, State2);

            builder.DefineState(State2)
            .OnEnter <int>(value => actual = value);

            var target = builder.Build(Initial);

            target.Raise(Event1, expected);

            // --act
            target.Relaying <int>().Raise(raiseWay, Event2);

            // --assert
            actual.Should().Be(expected);
        }
コード例 #5
0
        public void should_exit_all_parent_states(RaiseWay raiseWay)
        {
            var actual = new List <string>();

            async Task EnterAsync(IStateMachine <string> stateMachine, string state)
            {
                while (stateMachine.InMyState)
                {
                    await Task.Delay(1);
                }

                actual.Add(state + Exit);
            }

            // --arrange
            var builder = new Builder <string, string>(OnException);

            builder
            .DefineState(Initial)
            .AddTransition(Branch1Level3, Branch1Level3);

            builder
            .DefineState(Root)
            .OnEnter(_ => EnterAsync(_, Root))
            .OnExit(() => actual.Add(Root));

            builder
            .DefineState(Branch1Level1)
            .AsSubstateOf(Root)
            .OnEnter(_ => EnterAsync(_, Branch1Level1))
            .OnExit(() => actual.Add(Branch1Level1));

            builder
            .DefineState(Branch1Level2)
            .AsSubstateOf(Branch1Level1)
            .OnEnter(_ => EnterAsync(_, Branch1Level2))
            .OnExit(() => actual.Add(Branch1Level2));

            builder
            .DefineState(Branch1Level3)
            .AsSubstateOf(Branch1Level2)
            .OnEnter(_ => EnterAsync(_, Branch1Level3))
            .OnExit(() => actual.Add(Branch1Level3))
            .AddTransition(Free1, Free1);

            builder
            .DefineState(Free1)
            .OnEnter(_ => actual.Add(Free1));

            var target = builder.Build(Initial);

            target.Raise(raiseWay, Branch1Level3);

            // --act
            target.Raise(raiseWay, Free1);

            // --assert
            actual.Should()
            .Equal(Branch1Level3 + Exit, Branch1Level3, Branch1Level2 + Exit, Branch1Level2, Branch1Level1 + Exit, Branch1Level1, Root + Exit, Root, Free1);
        }
コード例 #6
0
        public void should_not_exit_common_root(RaiseWay raiseWay)
        {
            var actual = new List <string>();

            async Task RootEnterAsync(IStateMachine <string> stateMachine)
            {
                actual.Add(Root);

                while (stateMachine.InMyState)
                {
                    await Task.Delay(1);
                }

                actual.Add(Root + Exit);
            }

            // --arrange
            var builder = new Builder <string, string>(OnException);

            builder
            .DefineState(Initial)
            .AddTransition(Branch1Level2, Branch1Level2);

            builder
            .DefineState(Root)
            .OnEnter(RootEnterAsync)
            .OnExit(() => actual.Add(Root));

            builder
            .DefineState(Branch1Level1)
            .AsSubstateOf(Root)
            .OnExit(() => actual.Add(Branch1Level1));

            builder
            .DefineState(Branch1Level2)
            .AsSubstateOf(Branch1Level1)
            .OnExit(() => actual.Add(Branch1Level2))
            .AddTransition(Branch2Level2, Branch2Level2);

            builder
            .DefineState(Branch2Level1)
            .AsSubstateOf(Root)
            .OnEnter(_ => actual.Add(Branch2Level1));

            builder
            .DefineState(Branch2Level2)
            .AsSubstateOf(Branch2Level1)
            .OnEnter(_ => actual.Add(Branch2Level2));

            var target = builder.Build(Initial);

            target.Raise(raiseWay, Branch1Level2);

            // --act
            target.Raise(raiseWay, Branch2Level2);

            // --assert
            actual.Should().Equal(Root, Branch1Level2, Branch1Level1, Branch2Level1, Branch2Level2);
        }
コード例 #7
0
ファイル: TransitionTest.cs プロジェクト: Ed-Pavlov/Binstate
        public void raise_should_return_false_if_no_transition_found(RaiseWay raiseWay)
        {
            // --arrange
            var builder = new Builder <string, string>(OnException);

            builder.DefineState(Initial).AddTransition(State1, State1);
            builder.DefineState(State1).OnEnter(() => Assert.Fail("No transition should be performed"));

            var target = builder.Build(Initial);

            // --act
            var actual = target.Raise(raiseWay, "WrongEvent");

            // --assert
            actual.Should().BeFalse();
        }
コード例 #8
0
ファイル: MemoryTest.cs プロジェクト: Ed-Pavlov/Binstate
        public void should_not_boxing_passed_value_type_arguments(RaiseWay raiseWay)
        {
            var        expected1 = new ValueType1(389);
            var        expected2 = new ValueType2(659);
            ValueType1 actual1   = default;
            ValueType2 actual2   = default;
            ITuple <ValueType2, ValueType1> actualTuple = null;

            // --arrange
            var builder = new Builder <string, int>(OnException);

            builder.DefineState(Initial).AddTransition(Event1, State1);

            builder.DefineState(State1)
            .OnEnter <ValueType1>(value => { })
            .AddTransition(Event2, State2);

            builder.DefineState(Parent)
            .OnEnter <ValueType2>(value => actual2 = value);

            builder.DefineState(Child)
            .AsSubstateOf(Parent)
            .OnEnter <ValueType1>(value => actual1 = value);

            builder.DefineState(State2)
            .AsSubstateOf(Child)
            .OnEnter <ITuple <ValueType2, ValueType1> >(value => actualTuple = value);

            var target = builder.Build(Initial, true);

            // --act
            target.Raise(raiseWay, Event1, expected1);                         // pass to State1

            target.Relaying <ValueType1>().Raise(raiseWay, Event2, expected2); // pass everywhere

            // --assert
            // actual.Should().Be(expected); -- this method leads boxing
            actual1.Value.Should().Be(expected1.Value);
            actual2.Value.Should().Be(expected2.Value);
            actualTuple !.RelayedArgument.Value.Should().Be(expected1.Value);
            actualTuple.PassedArgument.Value.Should().Be(expected2.Value);
        }
コード例 #9
0
        public void should_finish_enter_before_call_exit_and_call_next_enter(RaiseWay raiseWay)
        {
            var actual = new List <string>();

            const string enter1 = nameof(enter1);
            const string exit1  = nameof(exit1);
            const string enter2 = nameof(enter2);

            // --arrange
            var builder = new Builder <string, int>(OnException);

            builder.DefineState(Initial).AddTransition(Event1, State1);

            builder
            .DefineState(State1)
            .OnEnter(
                _ =>
            {
                Thread.Sleep(299);
                actual.Add(enter1);
            })
            .OnExit(
                () =>
            {
                Thread.Sleep(382);
                actual.Add(exit1);
            })
            .AddTransition(Event2, State2);

            builder.DefineState(State2)
            .OnEnter(_ => actual.Add(enter2));

            var target = builder.Build(Initial);

            target.Raise(raiseWay, Event1);

            // --act
            target.Raise(raiseWay, Event2);

            // --assert
            actual.Should().BeEquivalentTo(enter1, exit1, enter2);
        }
コード例 #10
0
        public void should_call_enter_on_activation(RaiseWay raiseWay)
        {
            var actual = new List <string>();

            // --arrange
            var builder = new Builder <string, int>(OnException);

            builder.DefineState(Initial)
            .AddTransition(Event1, State1);

            builder.DefineState(State1)
            .OnEnter(_ => actual.Add(State1));

            var stateMachine = builder.Build(Initial);

            // --act
            stateMachine.Raise(raiseWay, Event1);

            // --assert
            actual.Should().BeEquivalentTo(State1);
        }
コード例 #11
0
        public void should_enter_all_parent_states(RaiseWay raiseWay)
        {
            var actual = new List <string>();

            // --arrange
            var builder = new Builder <string, string>(OnException);

            builder
            .DefineState(Initial)
            .AddTransition(Branch1Level3, Branch1Level3);

            builder
            .DefineState(Root)
            .OnEnter(_ => actual.Add(Root));

            builder
            .DefineState(Branch1Level1)
            .AsSubstateOf(Root)
            .OnEnter(_ => actual.Add(Branch1Level1));

            builder
            .DefineState(Branch1Level2)
            .AsSubstateOf(Branch1Level1)
            .OnEnter(_ => actual.Add(Branch1Level2));

            builder
            .DefineState(Branch1Level3)
            .AsSubstateOf(Branch1Level2)
            .OnEnter(_ => actual.Add(Branch1Level3));

            var target = builder.Build(Initial);

            // --act
            target.Raise(raiseWay, Branch1Level3);

            // --assert
            actual.Should().Equal(Root, Branch1Level1, Branch1Level2, Branch1Level3);
        }
コード例 #12
0
ファイル: TransitionTest.cs プロジェクト: Ed-Pavlov/Binstate
        public void should_call_action_on_transition_between_exit_and_enter(RaiseWay raiseWay)
        {
            const string exit       = "Exit";
            const string transition = "Transition";
            var          actual     = new List <string>();

            // --arrange
            var builder = new Builder <string, int>(OnException);

            builder.DefineState(Initial)
            .OnExit(() => actual.Add(exit))
            .AddTransition(Event1, State1, () => actual.Add(transition));

            builder.DefineState(State1)
            .OnEnter(() => actual.Add(State1));

            var target = builder.Build(Initial);

            // --act
            target.Raise(raiseWay, Event1);

            // --assert
            actual.Should().BeEquivalentTo(exit, transition, State1);
        }
コード例 #13
0
        public void async_enter_should_not_block(RaiseWay raiseWay)
        {
            // --arrange
            var entered = new ManualResetEvent(false);

            async Task AsyncEnter(IStateMachine <int> stateMachine)
            {
                entered.Set();
                while (stateMachine.InMyState)
                {
                    await Task.Delay(546);
                }
            }

            var builder = new Builder <string, int>(OnException);

            builder.DefineState(Initial).AddTransition(Event1, State1);

            builder
            .DefineState(State1)
            .OnEnter(AsyncEnter)
            .AddTransition(Event2, State2);

            builder.DefineState(State2);

            var target = builder.Build(Initial);

            // --act
            target.Raise(raiseWay, Event1);

            // --assert
            entered.WaitOne(TimeSpan.FromSeconds(4)).Should().BeTrue();

            // --cleanup
            target.Raise(Event2); // exit async method
        }
コード例 #14
0
        public void should_pass_argument_to_enter(RaiseWay raiseWay)
        {
            const string expected = "expected";
            var          actual   = expected + "bad";

            // --arrange
            var builder = new Builder <string, int>(OnException);

            builder
            .DefineState(Initial)
            .AddTransition(Event1, State1);

            builder
            .DefineState(State1)
            .OnEnter <string>((sm, param) => actual = param);

            var target = builder.Build(Initial);

            // --act
            target.Raise(raiseWay, Event1, expected);

            // --assert
            actual.Should().Be(expected);
        }
コード例 #15
0
        public void should_throw_exception_if_no_argument_specified_for_enter_action_with_argument(RaiseWay raiseWay)
        {
            // --arrange
            var builder = new Builder <string, int>(OnException);

            builder
            .DefineState(Initial)
            .AddTransition(Event1, State1);

            builder
            .DefineState(State1)
            .OnEnter <int>(value => { });

            var stateMachine = builder.Build(Initial);

            // --act
            Action target = () => stateMachine.Raise(raiseWay, Event1);

            // --assert
            target.Should()
            .ThrowExactly <TransitionException>()
            .WithMessage($"The enter action of the state '{State1}' is configured as required an argument but no argument was specified.");
        }
コード例 #16
0
 public static bool Raise <TState, TEvent, TA>(this IStateMachine <TState, TEvent> stateMachine, RaiseWay way, TEvent @event, TA arg)
 => Call(way, () => stateMachine.Raise(@event, arg), () => stateMachine.RaiseAsync(@event, arg).Result);
コード例 #17
0
        public void should_pass_null_if_active_state_has_no_argument_and_relayArgumentIsRequired_is_false(RaiseWay raiseWay)
        {
            var actual = "bad";

            // --arrange
            var builder = new Builder <string, int>(OnException);

            builder.DefineState(Initial).AddTransition(Event1, State1);
            builder.DefineState(State1).OnEnter <string>(value => actual = value);

            var stateMachine = builder.Build(Initial);

            // --act
            const bool relayArgumentIsRequired = false;

            stateMachine.Relaying <string>(relayArgumentIsRequired).Raise(raiseWay, Event1);

            // --assert
            actual.Should().BeNull();
        }
コード例 #18
0
        public void should_pass_relayed_passed_and_tuple_arguments_depending_on_enter_type(RaiseWay raiseWay)
        {
            var          expectedRelayed = new MemoryStream();
            const string expectedPassed  = "stringValue";

            IDisposable actualRelayed = null;
            object      actualPassed  = null;
            ITuple <object, IDisposable> actualTuple = null;

            // --arrange
            var builder = new Builder <string, int>(OnException);

            builder.DefineState(Initial).AddTransition(Event1, State1);

            builder.DefineState(State1)
            .OnEnter <MemoryStream>(_ => { })
            .AddTransition(Event2, Child);

            builder.DefineState(Root)
            .OnEnter <object>(value => actualPassed = value);

            builder.DefineState(Parent)
            .AsSubstateOf(Root)
            .OnEnter <ITuple <object, IDisposable> >(value => actualTuple = value);

            builder.DefineState(Child)
            .AsSubstateOf(Parent)
            .OnEnter <IDisposable>(value => actualRelayed = value);

            var target = builder.Build(Initial, true);

            target.Raise(raiseWay, Event1, expectedRelayed); // attach MemoryStream to State1

            // --act
            target.Relaying <MemoryStream>().Raise(raiseWay, Event2, expectedPassed); // pass string and relay MemoryStream to Child

            // --assert
            actualPassed.Should().Be(expectedPassed);
            actualRelayed.Should().Be(expectedRelayed);
            actualTuple !.PassedArgument.Should().Be(expectedPassed);
            actualTuple.RelayedArgument.Should().Be(expectedRelayed);
        }
コード例 #19
0
        public void should_throw_exception_if_target_state_has_no_argument_and_relaying_is_called(RaiseWay raiseWay)
        {
            // --arrange
            var builder = new Builder <string, int>(OnException);

            builder.DefineState(Initial).AddTransition(Event1, State1);

            builder.DefineState(State1)
            .OnEnter <int>(_ => { })
            .AddTransition(Event2, State2);

            builder.DefineState(State2)
            .OnEnter(() => { });

            var stateMachine = builder.Build(Initial);

            stateMachine.Raise(Event1, 93);

            // --act
            Action target = () => stateMachine.Relaying <int>().Raise(raiseWay, Event2);

            // --assert
            target.Should()
            .ThrowExactly <TransitionException>()
            .WithMessage(
                $"Transition from the state '{State1}' by the event '{Event2}' will activate following states [{State2}]. No one of them are defined "
                + "with the enter action accepting an argument, but argument was passed or relayed");
        }
コード例 #20
0
 private static bool Call(RaiseWay way, Func <bool> syncAction, Func <bool> asyncAction)
 => way switch
 {
コード例 #21
0
        public void should_pass_both_passed_and_relayed_arguments_with_variance_conversion(RaiseWay raiseWay)
        {
            var          expectedRelayedValue = new MemoryStream();
            const string expectedPassedValue  = "stringValue";

            ITuple <object, IDisposable> actual = null;

            // --arrange
            var builder = new Builder <string, int>(OnException);

            builder.DefineState(Initial).AddTransition(Event1, State1);

            builder.DefineState(State1)
            .OnEnter <Stream>(_ => { })
            .AddTransition(Event2, State2);

            builder.DefineState(State2)
            .OnEnter <ITuple <object, IDisposable> >(value => actual = value);

            var target = builder.Build(Initial);

            target.Raise(raiseWay, Event1, expectedRelayedValue); // attach an argument to State1

            // --act
            target.Relaying <Stream>().Raise(raiseWay, Event2, expectedPassedValue); // pass and relay arguments to State2

            // --assert
            actual.Should().NotBeNull();
            actual !.PassedArgument.Should().Be(expectedPassedValue);
            actual.RelayedArgument.Should().Be(expectedRelayedValue);
        }
コード例 #22
0
        public void should_throw_exception_if_argument_is_not_assignable_to_enter_action(RaiseWay raiseWay)
        {
            // --arrange
            var builder = new Builder <string, int>(OnException);

            builder
            .DefineState(Initial)
            .AddTransition(Event1, State1);

            builder
            .DefineState(State1)
            .OnEnter <string>((sm, value) => { });

            var stateMachine = builder.Build(Initial);

            // --act
            Action target = () => stateMachine.Raise(raiseWay, Event1, 983);

            // --assert
            target.Should()
            .ThrowExactly <TransitionException>()
            .WithMessage($"The state '{State1}' requires argument of type '{typeof(string)}' but no argument of compatible type has passed nor relayed");
        }
コード例 #23
0
        public void should_pass_argument_if_parent_and_child_argument_are_differ_but_assignable_and_enter_with_no_argument_on_the_pass(RaiseWay raiseWay)
        {
            IDisposable actualDisposable = null;
            Stream      actualStream     = null;
            var         expected         = new MemoryStream();

            // --arrange
            var builder = new Builder <string, string>(OnException);

            builder.DefineState(Initial).AddTransition(Child, Child);
            builder.DefineState(Root).OnEnter <IDisposable>(value => actualDisposable = value);
            builder.DefineState(Parent).AsSubstateOf(Root).OnEnter(sm => { });
            builder.DefineState(Child).AsSubstateOf(Parent).OnEnter <Stream>(value => actualStream = value);

            var target = builder.Build(Initial);

            // --act
            target.Raise(raiseWay, Child, expected);

            // --assert
            actualStream.Should().BeSameAs(expected);
            actualDisposable.Should().BeSameAs(expected);
        }
コード例 #24
0
        public void should_pass_argument_if_argument_is_differ_but_assignable_to_enter_action_argument(RaiseWay raiseWay)
        {
            IDisposable actual   = null;
            var         expected = new MemoryStream();

            // --arrange
            var builder = new Builder <string, int>(OnException);

            builder.DefineState(Initial).AddTransition(Event1, State1);
            builder.DefineState(State1).OnEnter <IDisposable>((sm, value) => actual = value);

            var target = builder.Build(Initial);

            // --act
            target.Raise(raiseWay, Event1, expected);

            // --assert
            actual.Should().BeSameAs(expected);
        }
コード例 #25
0
        public void should_throw_exception_if_parent_and_child_state_has_not_assignable_arguments_enable_loose_relaying_is_true_and_argument_is_passed(RaiseWay way)
        {
            // --arrange
            var builder = new Builder <string, int>(OnException);


            builder.DefineState(Initial).AddTransition(Event1, Child);

            builder.DefineState(Parent)
            .OnEnter <int>((stateMachine, value) => { });

            builder.DefineState(Child)
            .AsSubstateOf(Parent)
            .OnEnter <string>(value => { });

            // --act
            var sm = builder.Build(Initial, true);

            Action target = () => sm.Raise(Event1, "stringArgument");

            // --assert
            target
            .Should()
            .Throw <TransitionException>()
            .WithMessage($"The state '{Parent}' requires argument of type '{typeof(int)}' but no argument of compatible type has passed nor relayed");
        }
コード例 #26
0
        public void should_throw_exception_if_argument_specified_and_no_argument_required_by_all_activated_states(RaiseWay raiseWay)
        {
            // --arrange
            var builder = new Builder <string, int>(OnException);

            builder
            .DefineState(Initial)
            .AddTransition(Event1, Child);

            builder
            .DefineState(Parent)
            .OnEnter(sm => { });

            builder
            .DefineState(Child)
            .AsSubstateOf(Parent)
            .OnEnter(sm => { });

            var stateMachine = builder.Build(Initial);

            // --act
            Action target = () => stateMachine.Raise(raiseWay, Event1, "argument");

            // --assert
            target.Should()
            .ThrowExactly <TransitionException>()
            .WithMessage(
                $"Transition from the state '{Initial}' by the event '{Event1}' will activate following states [{Parent}->{Child}]. No one of them are defined with the enter "
                + "action accepting an argument, but argument was passed or relayed");
        }