diff --git a/codex/utils/asyncstatemachine.nim b/codex/utils/asyncstatemachine.nim index e861ed7c..4b1e5327 100644 --- a/codex/utils/asyncstatemachine.nim +++ b/codex/utils/asyncstatemachine.nim @@ -3,15 +3,21 @@ import pkg/chronos type AsyncStateMachine* = ref object of RootObj + state: AsyncState AsyncState* = ref object of RootObj + Event* = proc(state: AsyncState): ?AsyncState method run*(state: AsyncState): Future[?AsyncState] {.base.} = discard -proc runState(state: AsyncState): Future[void] {.async.} = +proc runState(machine: AsyncStateMachine, state: AsyncState): Future[void] {.async.} = + machine.state = state if next =? await state.run(): - await runState(next) + await machine.runState(next) proc start*(stateMachine: AsyncStateMachine, initialState: AsyncState) = - asyncSpawn runState(initialState) + asyncSpawn stateMachine.runState(initialState) +proc schedule*(stateMachine: AsyncStateMachine, event: Event) = + if next =? stateMachine.state.event(): + asyncSpawn stateMachine.runState(next) \ No newline at end of file diff --git a/tests/codex/utils/testasyncstatemachine.nim b/tests/codex/utils/testasyncstatemachine.nim index d5c49f34..8b585359 100644 --- a/tests/codex/utils/testasyncstatemachine.nim +++ b/tests/codex/utils/testasyncstatemachine.nim @@ -5,45 +5,57 @@ import codex/utils/asyncstatemachine import ../helpers/eventually type - TestState = ref object of AsyncState - State1 = ref object of AsyncState - State2 = ref object of AsyncState + AsyncTestState = ref object of AsyncState + State1 = ref object of AsyncTestState + State2 = ref object of AsyncTestState + State3 = ref object of AsyncTestState -var runInvoked = 0 +var state1runInvoked = 0 var state2runInvoked = 0 +var state3runInvoked = 0 -method run(state: TestState): Future[?AsyncState] {.async.} = - inc runInvoked +method onMoveToNextStateEvent*(state: AsyncTestState): ?AsyncState {.base.} = + discard method run(state: State1): Future[?AsyncState] {.async.} = + inc state1runInvoked return some AsyncState(State2.new()) method run(state: State2): Future[?AsyncState] {.async.} = inc state2runInvoked -suite "async state machines": - setup: - runInvoked = 0 - state2runInvoked = 0 +method onMoveToNextStateEvent(state: State2): ?AsyncState = + return some AsyncState(State3.new()) - test "creates async state machine": - let sm = AsyncStateMachine.new() - check sm != nil +method run(state: State3): Future[?AsyncState] {.async.} = + inc state3runInvoked + +suite "async state machines": + var machine: AsyncStateMachine + var state1, state2: AsyncState + + setup: + state1runInvoked = 0 + state2runInvoked = 0 + state3runInvoked = 0 + machine = AsyncStateMachine.new() + state1 = State1.new() + state2 = State2.new() test "should call run on start state": - let sm = AsyncStateMachine.new() - let testState = TestState.new() - - sm.start(testState) - - check eventually runInvoked == 1 + machine.start(state1) + check eventually state1runInvoked == 1 test "moves to next state when run completes": - let sm = AsyncStateMachine.new() - let state1 = State1.new() - let state2 = State2.new() - - sm.start(state1) - + machine.start(state1) check eventually state2runInvoked == 1 + test "state2 moves to state3 on event": + machine.start(state2) + + proc moveToNextStateEvent(state: AsyncState): ?AsyncState = + AsyncTestState(state).onMoveToNextStateEvent() + + machine.schedule(Event(moveToNextStateEvent)) + + check eventually state3runInvoked == 1