Skip to content

Commit

Permalink
feat: Add set state method (#176)
Browse files Browse the repository at this point in the history
  • Loading branch information
erikzenker committed Nov 20, 2022
1 parent 3f83bda commit b7e295f
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 27 deletions.
24 changes: 24 additions & 0 deletions include/hsm/details/sm.h
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,30 @@ template <class RootState, class... OptionalParameters> class sm {
return currentParentState() == getParentStateIdx(rootState, parentState);
}

template <class State> auto set(State state) -> void
{
set(0, rootState, state);
}

template <class ParentState, class State> auto set(ParentState parentState, State state) -> void
{
set(0, parentState, state);
}

template <class ParentState, class State>
auto set(Region region, ParentState parentState, State state) -> void
{
static_assert(
bh::contains(collect_state_typeids_recursive(rootState), bh::typeid_(state)),
"State does not exist in transition table");
static_assert(
bh::contains(collect_parent_state_typeids(rootState), bh::typeid_(parentState)),
"Parent state does not exist in transition table");

m_currentCombinedState.at(region)
= getCombinedStateIdx(getCombinedStateTypeids(rootState), parentState, state);
}

auto status() -> std::string
{
std::stringstream statusStream;
Expand Down
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ add_executable(
integration/guards_actions.cpp
integration/orthogonal_regions.cpp
integration/process_event_return_bool.cpp
integration/set_state.cpp
integration/internal_transition.cpp
integration/unexpected_event_handler.cpp
integration/history_pseudo_state.cpp
Expand Down
6 changes: 4 additions & 2 deletions test/integration/chain_actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ TEST_F(ComposeActionTests, should_call_chained_actions)

sm.process_event(e1 { a1Called, a2Called });

ASSERT_EQ(std::future_status::ready, a1Called->get_future().wait_for(std::chrono::seconds(1)));
ASSERT_EQ(std::future_status::ready, a2Called->get_future().wait_for(std::chrono::seconds(1)));
ASSERT_TRUE(
std::future_status::ready == a1Called->get_future().wait_for(std::chrono::seconds(1)));
ASSERT_TRUE(
std::future_status::ready == a2Called->get_future().wait_for(std::chrono::seconds(1)));
}
33 changes: 17 additions & 16 deletions test/integration/entry_exit_actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,12 +154,12 @@ TEST_F(EntryExitActionsTests, should_call_entry_and_exit_action)
sm.process_event(e1 { exitActionCalled });
ASSERT_TRUE(sm.is(hsm::state<S1>));

ASSERT_EQ(
std::future_status::ready,
entryActionCalled->get_future().wait_for(std::chrono::seconds(1)));
ASSERT_EQ(
std::future_status::ready,
exitActionCalled->get_future().wait_for(std::chrono::seconds(1)));
ASSERT_TRUE(
std::future_status::ready
== entryActionCalled->get_future().wait_for(std::chrono::seconds(1)));
ASSERT_TRUE(
std::future_status::ready
== exitActionCalled->get_future().wait_for(std::chrono::seconds(1)));
}

TEST_F(EntryExitActionsTests, should_call_entry_and_exit_action_of_substate)
Expand All @@ -173,12 +173,12 @@ TEST_F(EntryExitActionsTests, should_call_entry_and_exit_action_of_substate)
sm.process_event(e2 { exitActionCalled });
ASSERT_TRUE(sm.is(hsm::state<S1>));

ASSERT_EQ(
std::future_status::ready,
entryActionCalled->get_future().wait_for(std::chrono::seconds(1)));
ASSERT_EQ(
std::future_status::ready,
exitActionCalled->get_future().wait_for(std::chrono::seconds(1)));
ASSERT_TRUE(
std::future_status::ready
== entryActionCalled->get_future().wait_for(std::chrono::seconds(1)));
ASSERT_TRUE(
std::future_status::ready
== exitActionCalled->get_future().wait_for(std::chrono::seconds(1)));
}

TEST_F(EntryExitActionsTests, should_call_entry_action_of_substate_initial_state)
Expand All @@ -188,9 +188,9 @@ TEST_F(EntryExitActionsTests, should_call_entry_action_of_substate_initial_state
sm.process_event(e3 { entryActionCalled });
ASSERT_TRUE(sm.is(hsm::state<SubState2>, hsm::state<S2>));

ASSERT_EQ(
std::future_status::ready,
entryActionCalled->get_future().wait_for(std::chrono::seconds(1)));
ASSERT_TRUE(
std::future_status::ready
== entryActionCalled->get_future().wait_for(std::chrono::seconds(1)));
}

TEST_F(EntryExitActionsTests, should_call_exit_action_on_pseudo_exit)
Expand All @@ -201,5 +201,6 @@ TEST_F(EntryExitActionsTests, should_call_exit_action_on_pseudo_exit)
sm.process_event(e4 {});
ASSERT_TRUE(sm.is(hsm::state<S1>));

ASSERT_EQ(std::future_status::ready, dep.called.get_future().wait_for(std::chrono::seconds(1)));
ASSERT_TRUE(
std::future_status::ready == dep.called.get_future().wait_for(std::chrono::seconds(1)));
}
8 changes: 4 additions & 4 deletions test/integration/guards_actions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ TEST_F(GuardsActionsTests, should_call_action)

sm.process_event(e1 { actionCalled });

ASSERT_EQ(
std::future_status::ready, actionCalled->get_future().wait_for(std::chrono::seconds(1)));
ASSERT_TRUE(
std::future_status::ready == actionCalled->get_future().wait_for(std::chrono::seconds(1)));
}

TEST_F(GuardsActionsTests, should_call_substate_action)
Expand All @@ -171,8 +171,8 @@ TEST_F(GuardsActionsTests, should_call_substate_action)
sm.process_event(e2 {});
sm.process_event(e1 { actionCalled });

ASSERT_EQ(
std::future_status::ready, actionCalled->get_future().wait_for(std::chrono::seconds(1)));
ASSERT_TRUE(
std::future_status::ready == actionCalled->get_future().wait_for(std::chrono::seconds(1)));
}

TEST_F(GuardsActionsTests, should_block_transition_guard)
Expand Down
10 changes: 5 additions & 5 deletions test/integration/internal_transition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ TEST_F(InternalTransitionTests, should_call_action_on_internal_transition)
sm.process_event(e2 { actionCalled });
ASSERT_TRUE(sm.is(hsm::state<S1>));

ASSERT_EQ(
std::future_status::ready, actionCalled->get_future().wait_for(std::chrono::seconds(1)));
ASSERT_TRUE(
std::future_status::ready == actionCalled->get_future().wait_for(std::chrono::seconds(1)));
}

TEST_F(InternalTransitionTests, should_guard_internal_transition)
Expand All @@ -172,9 +172,9 @@ TEST_F(InternalTransitionTests, should_guard_internal_transition_with_action)
sm.process_event(e4 { actionCalled });
ASSERT_TRUE(sm.is(hsm::state<S1>));

ASSERT_EQ(
std::future_status::timeout,
actionCalled->get_future().wait_for(std::chrono::milliseconds(1)));
ASSERT_TRUE(
std::future_status::timeout
== actionCalled->get_future().wait_for(std::chrono::milliseconds(1)));
}

TEST_F(
Expand Down
90 changes: 90 additions & 0 deletions test/integration/set_state.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#include "hsm/hsm.h"

#include <boost/hana.hpp>
#include <boost/hana/experimental/printable.hpp>
#include <gtest/gtest.h>

namespace {

using namespace ::testing;
using namespace boost::hana;

// States
struct S1 {
};
struct S2 {
};
struct S3 {
};

// Events
struct e1 {
};
struct e2 {
};

struct SubState {
static constexpr auto make_transition_table()
{
// clang-format off
return hsm::transition_table(
* hsm::state<S1> + hsm::event<e1> = hsm::state<S2>
);
// clang-format on
}
};

struct MainState {
static constexpr auto make_transition_table()
{
// clang-format off
return hsm::transition_table(
// Source , Event , Target
* hsm::state<S1> + hsm::event<e1> = hsm::state<S2>
, hsm::state<S1> + hsm::event<e1> = hsm::state<SubState>
);
// clang-format on
}
};

}

class SetStateTests : public Test {
protected:
hsm::sm<MainState> sm;
};

TEST_F(SetStateTests, should_set_state)
{
ASSERT_TRUE(sm.is(hsm::state<S1>));
sm.set(hsm::state<S2>);
ASSERT_TRUE(sm.is(hsm::state<S2>));
}

TEST_F(SetStateTests, should_set_to_substate)
{
ASSERT_TRUE(sm.is(hsm::state<S1>));
sm.set(hsm::state<SubState>, hsm::state<S2>);
ASSERT_TRUE(sm.is(hsm::state<SubState>, hsm::state<S2>));
}

TEST_F(SetStateTests, should_fail_to_set_invalid_region)
{
ASSERT_THROW(sm.set(1, hsm::state<SubState>, hsm::state<S1>), std::out_of_range);
}

//
// Should fail to compile since state S3 is not part of the transition table
//
// TEST_F(SetStateTests, should_fail_to_compile)
// {
// sm.set(hsm::state<S3>);
// }

//
// Should fail to compile since parent state S3 is not part of the transition table
//
// TEST_F(SetStateTests, should_fail_to_compile)
// {
// sm.set(hsm::state<S3>, hsm::state<S1>);
// }

0 comments on commit b7e295f

Please sign in to comment.