Skip to content

Commit

Permalink
Merge pull request #10 from cozzyy2002/bugfix-0008-dispatch-on-manage…
Browse files Browse the repository at this point in the history
…d-thread

Bugfix #8 dispatch on managed thread
  • Loading branch information
cozzyy2002 committed Jan 11, 2020
2 parents bb21536 + 73782c1 commit 82ce264
Show file tree
Hide file tree
Showing 16 changed files with 240 additions and 346 deletions.
6 changes: 5 additions & 1 deletion StateMachine.NET.TestConsole/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,14 @@ static void Main(string[] args)

}
}

var hr = context.shutdown();
Console.WriteLine($"Context.shutdown() returns {hr}.\nKey in [Enter] to exit.");
Console.ReadLine();
}
}

class Context : tsm_NET.Generic.Context<Event, State>
class Context : tsm_NET.Generic.AsyncContext<Event, State>
{
}

Expand Down
62 changes: 40 additions & 22 deletions StateMachine.NET.UnitTest/Class1.cs
Original file line number Diff line number Diff line change
@@ -1,41 +1,38 @@
using NSubstitute;
using NUnit.Framework;
using System;
using System.Diagnostics;
using System.Threading;
using tsm_NET.Generic;

namespace StateMachine.NET.UnitTest.Generic
{
public class Context : Context<Event, State>
[TestFixture]
public class TestCase
{
public Context() : base(true) { }
public Context(bool isAsync) : base(isAsync) { }
}
public class Context : Context<Event, State>
{
}

public class Event : Event<Context>
{
public static Event Null { get; } = null;
}
public class Event : Event<Context>
{
public static Event Null { get; } = null;
}

public class State : State<Context, Event, State>
{
public static State Null { get; } = null;
}
public class State : State<Context, Event, State>
{
public static State Null { get; } = null;
}

[TestFixture]
public class GenericTestCase
{
[Test]
public void SyncContextTest()
public void BasicTest()
{
var mockEvent = Substitute.For<Event>();
var mockInitialState = Substitute.For<State>();
var mockNextState = Substitute.For<State>();
var mockStateMonitor = Substitute.For<IStateMonitor<Event, State>>();

// Create synchronous Context object.
var c = new Context(false);
var c = new Context();
c.StateMonitor = mockStateMonitor;
Assert.That(c.CurrentState, Is.EqualTo(null), "Context has no initial state when created.");

Expand Down Expand Up @@ -89,11 +86,31 @@ public void SyncContextTest()
mockStateMonitor.DidNotReceive().onIdle(Arg.Any<Context>());
mockStateMonitor.DidNotReceive().onWorkerThreadExit(Arg.Any<Context>(), Arg.Any<HResult>());
}
}

[TestFixture]
public class AsyncTestCase
{
public class Context : AsyncContext<Event, State>
{
// StateMachine should run on managed thread to test on NUnit.
public Context() : base(true) { }
}

public class Event : Event<Context>
{
public static Event Null { get; } = null;
}

public class State : State<Context, Event, State>
{
public static State Null { get; } = null;
}

[Test]
public void BasicTest()
{
var mockEvent = Substitute.For<Event>();
var mockEvent = Substitute.For<Event>();
var mockInitialState = Substitute.For<State>();
var mockNextState = Substitute.For<State>();
var mockStateMonitor = Substitute.For<IStateMonitor<Event, State>>();
Expand Down Expand Up @@ -122,7 +139,7 @@ public void BasicTest()
// Shutdown
mockNextState.IsExitCalledOnShutdown = true;
Assume.That(c.shutdown(TimeSpan.FromSeconds(1)), Is.EqualTo(HResult.Ok));
Thread.Sleep(1000);
Thread.Sleep(100);

// Check calls to methods of State.
Received.InOrder(() =>
Expand All @@ -143,8 +160,6 @@ public void BasicTest()
// Check calls to methods of IStateMonitor.
Received.InOrder(() =>
{
mockStateMonitor.Received()
.onEventTriggered(Arg.Is(c), Arg.Is(mockEvent));
mockStateMonitor.Received()
.onEventHandling(Arg.Is(c), Arg.Is(mockEvent), Arg.Is(mockInitialState));
mockStateMonitor.Received()
Expand All @@ -157,6 +172,9 @@ public void BasicTest()
// onStateChanged() caused by Context.setup() might be called before or after onEventTriggerd().
mockStateMonitor.Received()
.onStateChanged(Arg.Is(c), Arg.Is(Event.Null), Arg.Is(State.Null), Arg.Is(mockInitialState));
// onEventTriggered() might be called before or after onEventHandling().
mockStateMonitor.Received()
.onEventTriggered(Arg.Is(c), Arg.Is(mockEvent));
}
}
}
14 changes: 8 additions & 6 deletions StateMachine.NET.UnitTest/Class2.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
using NSubstitute;
using NUnit.Framework;
using System.Diagnostics;
using System;
using System.Threading;
using tsm_NET;

namespace StateMachine.NET.UnitTest
{
[TestFixture]
public class TestCase
public class AsyncTestCase
{
[Test]
public void BasicTest()
Expand All @@ -17,7 +17,8 @@ public void BasicTest()
var mockNextState = Substitute.For<State>();
var mockStateMonitor = Substitute.For<IStateMonitor>();

var c = new Context();
// StateMachine should run on managed thread to test on NUnit.
var c = new AsyncContext(true);
c.StateMonitor = mockStateMonitor;
Assert.That(c.CurrentState, Is.EqualTo(null), "Context has no initial state when created.");

Expand All @@ -26,7 +27,7 @@ public void BasicTest()
.handleEvent(Arg.Is(c), Arg.Is(mockEvent), ref Arg.Is((State)null))
.Returns(x =>
{
Trace.WriteLine($"{mockInitialState.GetType()}.handleEvent({x[0]}) is called.");
Console.WriteLine($"{mockInitialState.GetType()}.handleEvent({x[0]}) is called.");
x[2] = mockNextState;
return HResult.Ok;
});
Expand Down Expand Up @@ -62,8 +63,6 @@ public void BasicTest()
// Check calls to methods of IStateMonitor.
Received.InOrder(() =>
{
mockStateMonitor.Received()
.onEventTriggered(Arg.Is(c), Arg.Is(mockEvent));
mockStateMonitor.Received()
.onEventHandling(Arg.Is(c), Arg.Is(mockEvent), Arg.Is(mockInitialState));
mockStateMonitor.Received()
Expand All @@ -76,6 +75,9 @@ public void BasicTest()
// onStateChanged() caused by Context.setup() might be called before or after onEventTriggerd().
mockStateMonitor.Received()
.onStateChanged(Arg.Is(c), Arg.Is((Event)null), Arg.Is((State)null), Arg.Is(mockInitialState));
// onEventTriggered() might be called before or after onEventHandling().
mockStateMonitor.Received()
.onEventTriggered(Arg.Is(c), Arg.Is(mockEvent));
}
}
}
3 changes: 2 additions & 1 deletion StateMachine.NET.UnitTest/StateMachine.NET.UnitTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
</PreBuildEvent>
</PropertyGroup>
<PropertyGroup>
<PostBuildEvent>xcopy /D /Y "$(SolutionDir)packages\NUnit.ConsoleRunner.3.10.0\tools\*.*" "$(TargetDir)"</PostBuildEvent>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>
24 changes: 12 additions & 12 deletions StateMachine.NET/GenericObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,38 +66,38 @@ namespace Generic
}

generic<typename C, typename E, typename S>
HRESULT State<C, E, S>::handleEventCallback(tsm::IContext* context, tsm::IEvent* event, tsm::IState** nextState)
tsm_NET::HResult State<C, E, S>::handleEvent(tsm_NET::Context^ context, tsm_NET::Event^ event, tsm_NET::State^% nextState)
{
S _nextState;
auto hr = handleEvent((C)getManaged((native::Context*)context), (E)getManaged((native::Event*)event), _nextState);
auto hr = handleEvent((C)context, (E)event, _nextState);
if(_nextState) {
*nextState = _nextState->get();
nextState = _nextState;
}
return (HRESULT)hr;
return (tsm_NET::HResult)hr;
}

generic<typename C, typename E, typename S>
HRESULT State<C, E, S>::entryCallback(tsm::IContext* context, tsm::IEvent* event, tsm::IState* previousState)
tsm_NET::HResult State<C, E, S>::entry(tsm_NET::Context^ context, tsm_NET::Event^ event, tsm_NET::State^ previousState)
{
return (HRESULT)entry((C)getManaged((native::Context*)context), (E)getManaged((native::Event*)event), (S)getManaged((native::State*)previousState));
return (tsm_NET::HResult)entry((C)context, (E)event, (S)previousState);
}

generic<typename C, typename E, typename S>
HRESULT State<C, E, S>::exitCallback(tsm::IContext* context, tsm::IEvent* event, tsm::IState* nextState)
tsm_NET::HResult State<C, E, S>::exit(tsm_NET::Context^ context, tsm_NET::Event^ event, tsm_NET::State^ nextState)
{
return (HRESULT)exit((C)getManaged((native::Context*)context), (E)getManaged((native::Event*)event), (S)getManaged((native::State*)nextState));
return (tsm_NET::HResult)exit((C)context, (E)event, (S)nextState);
}

generic<typename C>
HRESULT Event<C>::preHandleCallback(tsm::IContext* context)
tsm_NET::HResult Event<C>::preHandle(tsm_NET::Context^ context)
{
return (HRESULT)preHandle((C)getManaged((native::Context*)context));
return (tsm_NET::HResult)preHandle((C)context);
}

generic<typename C>
HRESULT Event<C>::postHandleCallback(tsm::IContext* context, HRESULT hr)
tsm_NET::HResult Event<C>::postHandle(tsm_NET::Context^ context, tsm_NET::HResult hr)
{
return (HRESULT)postHandle((C)getManaged((native::Context*)context), (HResult)hr);
return (tsm_NET::HResult)postHandle((C)context, (HResult)hr);
}
}
}
32 changes: 18 additions & 14 deletions StateMachine.NET/GenericObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@ public ref class StateMonitorCaller : public tsm_NET::StateMonitorCaller
generic<typename E, typename S>
public ref class Context : public tsm_NET::Context
{
protected:
Context(bool isAsync, bool useNativeThread) : tsm_NET::Context(isAsync, useNativeThread), m_stateMonitor(nullptr) {}

public:
Context() : tsm_NET::Context(true), m_stateMonitor(nullptr) {}
Context(bool isAsync ) : tsm_NET::Context(isAsync), m_stateMonitor(nullptr) {}
Context() : tsm_NET::Context(false, false), m_stateMonitor(nullptr) {}
virtual ~Context() {}

HResult setup(S initialState, E event) { return (HResult)tsm_NET::Context::setup((tsm_NET::State^)initialState, (tsm_NET::Event^)event); }
Expand All @@ -72,6 +74,14 @@ public ref class Context : public tsm_NET::Context
StateMonitorCaller<E, S>^ m_stateMonitorCaller;
};

generic<typename E, typename S>
public ref class AsyncContext : public Context<E, S>
{
public:
AsyncContext() : Context(true, false) {}
AsyncContext(bool useNativeThread) : Context(true, useNativeThread) {}
};

generic<typename C, typename E, typename S>
where C : tsm_NET::Context
where E : tsm_NET::Event
Expand All @@ -81,7 +91,6 @@ public ref class State : public tsm_NET::State
public:
State() : tsm_NET::State(nullptr) {}
State(S masterState) : tsm_NET::State((tsm_NET::State^)masterState) {}
~State() {}

#pragma region Methods to be implemented by sub class.
virtual HResult handleEvent(C context, E event, S% nextState) { return HResult::Ok; }
Expand All @@ -93,13 +102,10 @@ public ref class State : public tsm_NET::State

property S MasterState { S get() { return getMasterState(); } }

// NOTE: Callback methods that is called by native class should be `internal`
// to avoid `System.MissingMethodException` when NUnit runs with NSubstitute.
internal:
#pragma region Methods that call sub class with generic parameters.
virtual HRESULT handleEventCallback(tsm::IContext* context, tsm::IEvent* event, tsm::IState** nextState) override;
virtual HRESULT entryCallback(tsm::IContext* context, tsm::IEvent* event, tsm::IState* previousState) override;
virtual HRESULT exitCallback(tsm::IContext* context, tsm::IEvent* event, tsm::IState* nextState) override;
#pragma region Override methods of tsm_NET::State that call sub class with generic parameters.
virtual tsm_NET::HResult handleEvent(tsm_NET::Context^ context, tsm_NET::Event^ event, tsm_NET::State^% nextState) override sealed;
virtual tsm_NET::HResult entry(tsm_NET::Context^ context, tsm_NET::Event^ event, tsm_NET::State^ previousState) override sealed;
virtual tsm_NET::HResult exit(tsm_NET::Context^ context, tsm_NET::Event^ event, tsm_NET::State^ nextState) override sealed;
#pragma endregion
};

Expand All @@ -108,17 +114,15 @@ generic<typename C>
public ref class Event : public tsm_NET::Event
{
public:
~Event() {}

#pragma region Methods to be implemented by sub class.
virtual HResult preHandle(C context) { return HResult::Ok; }
virtual HResult postHandle(C context, HResult hr) { return hr; }
#pragma endregion

internal:
#pragma region Methods that call sub class with generic parameters.
virtual HRESULT preHandleCallback(tsm::IContext* context) override;
virtual HRESULT postHandleCallback(tsm::IContext* context, HRESULT hr) override;
virtual tsm_NET::HResult preHandle(tsm_NET::Context^ context) override sealed;
virtual tsm_NET::HResult postHandle(tsm_NET::Context^ context, tsm_NET::HResult hr) override sealed;
#pragma endregion
};
}
Expand Down
48 changes: 0 additions & 48 deletions StateMachine.NET/NativeCallback.h

This file was deleted.

0 comments on commit 82ce264

Please sign in to comment.