/** * @file tests/can_net_self_leader_giveup.cpp * @brief CAN Net self node leader giveup * @date Dec 14, 2016 * @author jjacobs * @copyright 2016 Dual Inventive Technology Centre B.V. * */ #include "fixtures/CANTest.hpp" extern "C" { #include "time.c" } /** * Give up ROLE_LEADER from application * * Verify if leader is able to give up leadership * * Force self ROLE_LEADER, DISCOVER_FINISH * * Set role from LEADER -> FOLLOWER * * Check if self role update is published (heartbeat msg, contains type and role) * * Check if self_was_leader is set */ TEST_F(DI_CAN, net_self_leader_giveup) { SetNodeid(0xdeadbeef); /* Set self TYPE_GATEWAY, force self ROLE_LEADER, DISCOVER_STATE_FINISH */ ASSERT_EQ(DNOK, di_can_net_self_set_node_type(&_ctx, DI_CAN_NET_NODE_TYPE_GATEWAY)); _ctx.net.discover.state = DI_CAN_NET_DISCOVER_STATE_FINISH; _ctx.net.self.role = DI_CAN_NET_NODE_ROLE_LEADER; ASSERT_TRUE(di_can_net_self_is_leader(&_ctx)); /* Set ROLE_FOLLOWER, check if self_was_leader is set */ ASSERT_EQ(DNOK, di_can_net_self_set_node_role(&_ctx, DI_CAN_NET_NODE_ROLE_FOLLOWER)); ASSERT_TRUE(_ctx.net.self_was_leader); /* Verify ROLE_FOLLOWER message is published */ struct di_can_msg *msg = di_can_recv(&_ctx, DI_CAN_MSGTYPE_NET); ASSERT_NE(nullptr, msg); ASSERT_EQ(DI_CAN_TRANSFERTYPE_PUBLISH, DI_CAN_GET_TRANSFERTYPE(msg->canid)); ASSERT_EQ(DI_CAN_NET_DTYPE_HEARTBEAT, DI_CAN_GET_DATATYPE(msg->canid)); ASSERT_TRUE(msg->broadcast); ASSERT_EQ(0xdeadbeef, msg->src_id); ASSERT_EQ(DI_CAN_NODEID_BROADCAST, msg->dst_id); /* Verify the payload struct */ ASSERT_EQ(DI_CAN_PTYPE_STRUCT, msg->ptype); ASSERT_EQ(2U, msg->size); ASSERT_EQ(DI_CAN_NET_NODE_TYPE_GATEWAY, msg->msg[0]); ASSERT_EQ(DI_CAN_NET_NODE_ROLE_FOLLOWER, msg->msg[1]); di_can_msg_free(&msg); /* Set ROLE_FOLLOWER again so we allow to become leader again */ ASSERT_EQ(DNOK, di_can_net_self_set_node_role(&_ctx, DI_CAN_NET_NODE_ROLE_FOLLOWER)); ASSERT_FALSE(_ctx.net.self_was_leader); /* Verify no message is published because we are already the follower */ msg = di_can_recv(&_ctx, DI_CAN_MSGTYPE_NET); ASSERT_EQ(nullptr, msg); } /** * Give up ROLE_LEADER from application check correct events are emitted * * Force self ROLE_LEADER, DISCOVER_FINISH * * Set role from LEADER -> FOLLOWER * * Update node list and try for self to become LEADER */ static size_t g_ev_idx = 0; static void net_self_leader_giveup_event_cb(const struct di_can_net *ctx, const enum di_can_net_node_events ev, const struct di_can_net_node *node) { (void)ctx; static const struct _tv { const enum di_can_net_node_events ev; const enum di_can_net_node_roles role; } tv[] = { { DI_CAN_NET_NODE_EVENT_LEADER_TAKEOVER_FAILED, DI_CAN_NET_NODE_ROLE_FOLLOWER }, { DI_CAN_NET_NODE_EVENT_LEADER_TAKEOVER_FAILED, DI_CAN_NET_NODE_ROLE_FOLLOWER }, { DI_CAN_NET_NODE_EVENT_LEADER_TAKEOVER_FAILED, DI_CAN_NET_NODE_ROLE_FOLLOWER }, { DI_CAN_NET_NODE_EVENT_ROLE_LEADER, DI_CAN_NET_NODE_ROLE_FOLLOWER } }; printf("event(%d): node: %04x, type: %02x, role: %02x\n", ev, node->nodeid, node->type, node->role); ASSERT_TRUE(di_can_net_node_is_self(ctx, node)); ASSERT_EQ(0xdeadbeef, node->nodeid); ASSERT_EQ(DI_CAN_NET_NODE_TYPE_GATEWAY, node->type); ASSERT_GT(DI_ARRAY_SIZE(tv), g_ev_idx); ASSERT_EQ(tv[g_ev_idx].ev, ev); ASSERT_EQ(tv[g_ev_idx].role, node->role); g_ev_idx++; } TEST_F(DI_CAN, net_self_leader_giveup_events) { SetNodeid(0xdeadbeef); di_can_net_node_set_event_callback(&_ctx, net_self_leader_giveup_event_cb); /* Set self TYPE_GATEWAY, set self ROLE_FOLLOWER (and self_was_leader), DISCOVER_STATE_FINISH */ ASSERT_EQ(DNOK, di_can_net_self_set_node_type(&_ctx, DI_CAN_NET_NODE_TYPE_GATEWAY)); _ctx.net.discover.state = DI_CAN_NET_DISCOVER_STATE_FINISH; _ctx.net.self.role = DI_CAN_NET_NODE_ROLE_FOLLOWER; _ctx.net.self_was_leader = true; /* Force discover timeout, execute net subsystem (results in leadership) */ _di_time_uptime = DI_CAN_NET_CFG_DISCOVER_TIMEOUT_MS; /* XXX NOTE: Because we run from the ROLE_FOLLOWER FSM, we get 3 times the LEADER_TAKEOVER_FAILED event */ ASSERT_EQ(0U, g_ev_idx); di_can_net_execute(&_ctx); di_can_net_execute(&_ctx); di_can_net_execute(&_ctx); ASSERT_EQ(3U, g_ev_idx); /* Set the ROLE_FOLLOWER so we can become the ROLE_LEADER */ ASSERT_EQ(DNOK, di_can_net_self_set_node_role(&_ctx, DI_CAN_NET_NODE_ROLE_FOLLOWER)); di_can_net_execute(&_ctx); ASSERT_EQ(4U, g_ev_idx); /* Verify ROLE_LEADER update is published because */ struct di_can_msg *msg = di_can_recv(&_ctx, DI_CAN_MSGTYPE_NET); ASSERT_NE(nullptr, msg); ASSERT_EQ(DI_CAN_TRANSFERTYPE_PUBLISH, DI_CAN_GET_TRANSFERTYPE(msg->canid)); ASSERT_EQ(DI_CAN_NET_DTYPE_HEARTBEAT, DI_CAN_GET_DATATYPE(msg->canid)); ASSERT_TRUE(msg->broadcast); ASSERT_EQ(0xdeadbeef, msg->src_id); ASSERT_EQ(DI_CAN_NODEID_BROADCAST, msg->dst_id); /* Verify the payload struct */ ASSERT_EQ(DI_CAN_PTYPE_STRUCT, msg->ptype); ASSERT_EQ(2U, msg->size); ASSERT_EQ(DI_CAN_NET_NODE_TYPE_GATEWAY, msg->msg[0]); ASSERT_EQ(DI_CAN_NET_NODE_ROLE_LEADER, msg->msg[1]); di_can_msg_free(&msg); }