/** * @file tests/can_msg.cpp * @brief brief * @date Sep 30, 2015 * @author jjacobs * @copyright 2015 Dual Inventive Technology Centre B.V. * * CAN Message buffer test */ #include #include "tests/can.h" #include #include #include #include TEST(time, init) { di_time_init(); } TEST(can_msg_gc, all) { } #if 0 extern "C" { #include "time.c" #include "can/msg.c" } static struct di_can_ctx ctx; DI_CAN_FRAME_RX_DECL_ARRAY(can_frames, DI_CAN_CFG_FRAME_SIZE); DI_CAN_MSG_DECL_ARRAY(can_msg_list, DI_CAN_CFG_MSG_SIZE, DI_CAN_CFG_MSG_DATA_SIZE); /** * Put test message into the pool for garbage collection * @todo we should move this into tests/can_msg_gc.cpp */ static void test_can_msg_gc_put(struct di_can_msg **msg) { uint8_t _msg[8] = "1234567"; *msg = di_can_msg_alloc(&ctx); if (*msg) { ASSERT_NE((void *)NULL, (*msg)->buf); di_buffer_memcpy((*msg)->buf, 0, _msg, sizeof(_msg)); } ASSERT_NE((void *)NULL, *msg); ASSERT_NE((void *)NULL, (*msg)->buf); ASSERT_STREQ((const char *)_msg, (const char *)(*msg)->buf->data); ASSERT_EQ(0U, (*msg)->timeout); } /** * The time subsystem needs to be initialized in order to have * the garbage collect feature work */ TEST(time, init) { di_time_init(); } /** * Test if initializing array with null or invalid arguments doesn't crash * 1. msg_list = NULL * 2. buffer_list = NULL * 3. msg_size != buffer_size */ TEST(can_msg, init_array_inval) { di_can_msg_init_array(NULL, 0, NULL, 0); di_can_msg_init_array(can_msg_list, DI_CAN_CFG_MSG_SIZE, NULL, DI_CAN_CFG_MSG_SIZE); di_can_msg_init_array(can_msg_list, DI_CAN_CFG_MSG_SIZE, can_msg_list_buffers, DI_CAN_CFG_MSG_SIZE - 1); } /** * Initialize the CAN stack msg subsystem invalid */ TEST(can_msg, init_null) { struct di_can_msg *msg; /* ctx = NULL */ di_can_msg_init(NULL, NULL, 0); /* list = NULL, size = 0 */ di_can_msg_init(&ctx, NULL, 0); ASSERT_EQ(0U, ctx.msg.pool.size); ASSERT_EQ((void *)NULL, ctx.msg.pool.begin); /* list != NULL, size = 0 */ di_can_msg_init(&ctx, (struct di_can_msg *)0x12345678, 0); ASSERT_EQ(0U, ctx.msg.pool.size); ASSERT_EQ((void *)NULL, ctx.msg.pool.begin); /* list = NULL, size != 0 */ di_can_msg_init(&ctx, NULL, DI_CAN_CFG_MSG_SIZE); ASSERT_EQ(0U, ctx.msg.pool.size); ASSERT_EQ((void *)NULL, ctx.msg.pool.begin); /* Check if the subsystem wont crash when not initialized */ msg = di_can_msg_alloc(&ctx); ASSERT_EQ((void *)NULL, msg); msg = di_can_msg_search(&ctx, 0x00000000); ASSERT_EQ((void *)NULL, msg); msg = di_can_msg_get(&ctx); ASSERT_EQ((void *)NULL, msg); msg = di_can_msg_get_ready(&ctx, DI_CAN_MSGTYPE_RAW); ASSERT_EQ((void *)NULL, msg); msg = di_can_msg_get_ready(&ctx, DI_CAN_MSGTYPE_RPC); ASSERT_EQ((void *)NULL, msg); } /** * Initialize the CAN stack msg subsystem */ TEST(can_msg, init) { struct di_can_msg *msg; /* Initialize the message array */ DI_CAN_MSG_INIT_ARRAY(can_msg_list, DI_CAN_CFG_MSG_SIZE, DI_CAN_CFG_MSG_DATA_SIZE); for (size_t n = 0; n < DI_CAN_CFG_MSG_SIZE; n++) { msg = &can_msg_list[n]; ASSERT_EQ(msg->state, DI_CAN_MSG_STATE_UNUSED); ASSERT_EQ(0U, msg->canid); ASSERT_EQ(DI_CAN_TIMEOUT_GC_SKIP, msg->timeout); ASSERT_EQ((void *)NULL, msg->msg); ASSERT_EQ(0U, msg->size); ASSERT_EQ(0U, msg->size_written); ASSERT_NE((void *)NULL, msg->buf); ASSERT_EQ((void *)NULL, msg->private_data); } /* Initialize the msg subsystem */ di_can_msg_init(&ctx, can_msg_list, DI_CAN_CFG_MSG_SIZE); ASSERT_EQ(DI_CAN_CFG_MSG_SIZE, ctx.msg.pool.size); ASSERT_EQ(can_msg_list, ctx.msg.pool.begin); } /** * Initialize again */ TEST(can_msg, init_again) { /* Initialize again with invalid parameters and see if nothing changed */ di_can_msg_init(&ctx, NULL, 0); ASSERT_EQ(DI_CAN_CFG_MSG_SIZE, ctx.msg.pool.size); ASSERT_EQ(can_msg_list, ctx.msg.pool.begin); /* Set msg.pool.begin = NULL, dont touch size */ ctx.msg.pool.begin = NULL; di_can_msg_init(&ctx, can_msg_list, DI_CAN_CFG_MSG_SIZE); ASSERT_EQ(DI_CAN_CFG_MSG_SIZE, ctx.msg.pool.size); ASSERT_EQ(can_msg_list, ctx.msg.pool.begin); /* Set msg.pool.size = 0, dont touch begin */ ctx.msg.pool.size = 0; di_can_msg_init(&ctx, can_msg_list, DI_CAN_CFG_MSG_SIZE); ASSERT_EQ(DI_CAN_CFG_MSG_SIZE, ctx.msg.pool.size); ASSERT_EQ(can_msg_list, ctx.msg.pool.begin); } /** * Check if reset of message will reset the correct fields * it will not scrub msg->buf->data */ TEST(can_msg, _reset) { const char *text = "Hello World!"; struct di_can_msg *msg; msg = di_can_msg_alloc(&ctx); ASSERT_NE((void *)NULL, msg); /* Set buffer of di_can_msg to "Hello World!" */ msg->size = 1234; di_buffer_memcpy(msg->buf, 0, text, strlen(text) + 1); _di_can_msg_reset(msg); ASSERT_EQ(0U, msg->size); ASSERT_STREQ(text, (const char *)msg->buf->data); di_can_msg_free(&msg); /* Get message from the pool, and check if still "Hello World!" is set */ msg = di_can_msg_alloc(&ctx); ASSERT_EQ(0U, msg->size); ASSERT_STREQ(text, (const char *)msg->buf->data); } /** * Check if NULL message return will not crash */ TEST(can_msg, return_null) { struct di_can_msg m; struct di_can_msg *msg = NULL; /* NULL */ di_can_msg_return_ready((struct di_can_msg **)NULL); di_can_msg_return_unused((struct di_can_msg **)NULL); /* msg = NULL */ di_can_msg_return_ready(&msg); di_can_msg_return_unused(&msg); /* msg.private_data = NULL */ m.buf = NULL; _di_can_msg_reset(&m); msg = &m; di_can_msg_return_ready(&msg); di_can_msg_return_unused(&msg); } /** Test if ctx = NULL argument is correctly handled */ TEST(can_msg, get_invalid) { ASSERT_EQ((void *)NULL, di_can_msg_get_unused(NULL)); ASSERT_EQ((void *)NULL, di_can_msg_get_used(NULL, 0)); ASSERT_EQ((void *)NULL, di_can_msg_get_ready(NULL, 0)); } /** * Check internal function * * Test if canid doesn't exist (canid 0x12345678) * * Put message into used state (canid 0x12345679) * * Check if canid exists/used * * Return * * Check if canid is not used */ TEST(can_msg, get_used) { uint32_t canid = 0; struct di_can_msg *msg; DI_CAN_SET_MSGTYPE(canid, DI_CAN_MSGTYPE_RPC); DI_CAN_SET_DATATYPE(canid, 1); DI_CAN_SET_TRANSACTION(canid, 2); /* Doesn't exist */ msg = di_can_msg_get_used(&ctx, canid); ASSERT_EQ((void *)NULL, msg); /* Get unused message and write canid */ msg = di_can_msg_get_unused(&ctx); ASSERT_NE((void *)NULL, msg); msg->canid = canid; /* Check if it is used */ msg = di_can_msg_get_used(&ctx, canid); ASSERT_NE((void *)NULL, msg); ASSERT_EQ(canid, msg->canid); /* Doesn't exist */ msg = di_can_msg_get_used(&ctx, 0xffffffff); ASSERT_EQ((void *)NULL, msg); /* * Change on of the canid fields and check if it doesn't * exist in the used state. This also test the correct * functioning of the canid macros which are used by the * framing layer. */ /* Change msgtype and check if it is not in the ctx */ DI_CAN_SET_MSGTYPE(canid, DI_CAN_MSGTYPE_RAW); msg = di_can_msg_get_used(&ctx, canid); ASSERT_EQ((void *)NULL, msg); DI_CAN_SET_MSGTYPE(canid, DI_CAN_MSGTYPE_RPC); /* Change datatype and check if is not in the ctx */ DI_CAN_SET_DATATYPE(canid, 2); msg = di_can_msg_get_used(&ctx, canid); ASSERT_EQ((void *)NULL, msg); DI_CAN_SET_DATATYPE(canid, 1); /* Change transaction and check if it is not in the ctx */ DI_CAN_SET_TRANSACTION(canid, 3); msg = di_can_msg_get_used(&ctx, canid); ASSERT_EQ((void *)NULL, msg); DI_CAN_SET_TRANSACTION(canid, 2); /* Return message */ msg = di_can_msg_get_used(&ctx, canid); ASSERT_NE((void *)NULL, msg); ASSERT_EQ(canid, msg->canid); di_can_msg_return_unused(&msg); /* Check if removed from ctx */ msg = di_can_msg_get_used(&ctx, canid); ASSERT_EQ((void *)NULL, msg); /* Garbage collect all messages */ di_can_msg_gc(&ctx, DI_CAN_GC_ALL); } /** Garbage collect where context is NULL */ TEST(can_msg, gc_null) { di_can_msg_gc(NULL, DI_CAN_GC_SINGLE); di_can_msg_gc(NULL, DI_CAN_GC_ALL); } /** * Normal garbage collect test * This uses the single garbage collect run */ TEST(can_msg, gc_single) { struct di_can_msg *msg; /* Test garbage collection on m->timeout = 1000ms uptime = 2000ms */ test_can_msg_gc_put(&msg); msg->timeout = 1000; _di_time_uptime = 2000; di_can_msg_gc(&ctx, DI_CAN_GC_SINGLE); ASSERT_EQ(0U, msg->timeout); /* Test garbage collection on m->timeout = 1000ms uptime = 1000ms */ test_can_msg_gc_put(&msg); msg->timeout = 1000; _di_time_uptime = 1000; di_can_msg_gc(&ctx, DI_CAN_GC_SINGLE); ASSERT_EQ(0U, msg->timeout); /* Test garbage collection skip on m->timeout = 1000ms uptime = 750ms */ test_can_msg_gc_put(&msg); _di_time_uptime = 750; msg->timeout = 1000; di_can_msg_gc(&ctx, DI_CAN_GC_SINGLE); ASSERT_EQ(1000U, msg->timeout); /* Now garbage collect previous message */ _di_time_uptime = 1000; di_can_msg_gc(&ctx, DI_CAN_GC_SINGLE); msg = di_can_msg_get_used(&ctx, 0); ASSERT_EQ((void *)NULL, msg); } /** * Garbage collection skip and immediate macro test * * Put message * * Set garbage collect to skip * * Garbage collect */ TEST(can_msg, gc_timeout_skip_and_immediate) { struct di_buffer *b; struct di_can_msg *msg; /* Test if message GC is skipped */ test_can_msg_gc_put(&msg); msg->timeout = DI_CAN_TIMEOUT_GC_SKIP; b = msg->buf; _di_time_uptime = 1000; di_can_msg_gc(&ctx, DI_CAN_GC_ALL); msg = di_can_msg_get_used(&ctx, 0); ASSERT_NE((void *)NULL, msg); ASSERT_EQ(b, msg->buf); /* Test if message GC is immediate */ _di_time_uptime = 0; msg->timeout = DI_CAN_TIMEOUT_GC_IMMEDIATE; di_can_msg_gc(&ctx, DI_CAN_GC_ALL); msg = di_can_msg_get_used(&ctx, 0); ASSERT_EQ((void *)NULL, msg); } /** * Get unused messages after garbage collection * * Deplete buffers * * Set all buffers timeout to IMMEDIATE * * Fetch all buffers * * Return all buffers */ TEST(can_msg, gc_get_unused) { struct di_can_msg *msg; /* Deplete buffers */ for (size_t n = 0; n < DI_CAN_CFG_MSG_SIZE; n++) { msg = di_can_msg_get_unused(&ctx); ASSERT_NE((void *)NULL, msg); msg->timeout = DI_CAN_TIMEOUT_GC_IMMEDIATE; } /* Check depletion without gc by using internal function */ msg = _di_can_msg_get_unused(&ctx); ASSERT_EQ((void *)NULL, msg); /* Get single garbage collected msg */ msg = di_can_msg_get_unused(&ctx); ASSERT_NE((void *)NULL, msg); msg->timeout = DI_CAN_TIMEOUT_GC_IMMEDIATE; /* Let the garbage collect of all frames be performed */ di_can_msg_gc(&ctx, DI_CAN_GC_ALL); /* Check all free again, and return */ for (size_t n = 0; n < DI_CAN_CFG_MSG_SIZE; n++) { msg = di_can_msg_get_unused(&ctx); ASSERT_NE((void *)NULL, msg); di_can_msg_return_unused(&msg); ASSERT_EQ((void *)NULL, msg); } di_can_msg_gc(&ctx, DI_CAN_GC_ALL); } /** * Test if ready msg is correctly stored * * Get unused message * * Return message in ready state * * Get message from ready state * * Return message as unused */ TEST(can_msg, return_get_ready) { struct di_can_msg *msg; msg = di_can_msg_get_unused(&ctx); ASSERT_NE((void *)NULL, msg); msg->canid = 0xdeadbeef; di_can_msg_return_ready(&msg); ASSERT_EQ((void *)NULL, msg); msg = di_can_msg_get_ready(&ctx, 0xdeadbeef); ASSERT_NE((void *)NULL, msg); di_can_msg_return_unused(&msg); ASSERT_EQ((void *)NULL, msg); } /** * Bug: 2 frames should result in two messages * the frame -> msg reassambler writes them into one di_can_msg because * th. */ TEST(can_msg, frames_to_one_msg_bug) { struct di_can_frame_rx *frame1; struct di_can_frame_rx *frame2; struct di_can_msg *msg1; struct di_can_msg *msg2; /* Reinitialize can stack by hand * reused from tests/can.h TEST(can, init) */ test_can_dump_file = stderr; di_time_init(); DI_CAN_MSG_INIT_ARRAY(can_msg_list, DI_CAN_CFG_MSG_SIZE, DI_CAN_CFG_MSG_DATA_SIZE); ASSERT_EQ(0, di_can_init(&ctx, TEST_CAN_NODEID)); di_can_frame_init(&ctx, can_frames, DI_CAN_CFG_FRAME_SIZE); di_can_msg_init(&ctx, can_msg_list, DI_CAN_CFG_MSG_SIZE); /* di_can_rx_frame, check not NULL and frame1 != frame2 */ frame1 = di_can_frame_get_unused(&ctx); frame2 = di_can_frame_get_unused(&ctx); ASSERT_NE((void *)NULL, frame1); ASSERT_NE((void *)NULL, frame2); ASSERT_NE(frame1, frame2); /* manual create, frame 2 (raw, pub, datatype 0x3ff, last frame, size 4, data "abc" */ DI_CAN_SET_MSGTYPE(frame1->canid, DI_CAN_MSGTYPE_RAW); DI_CAN_SET_TRANSFERTYPE(frame1->canid, DI_CAN_TRANSFERTYPE_PUB); DI_CAN_SET_DATATYPE(frame1->canid, 0x3ff); DI_CAN_SET_LAST_FRAME(frame1->canid, DI_CAN_LAST_FRAME_TRUE); snprintf((char *)frame1->buf, sizeof(frame1->buf), "abc"); frame1->size = 4; di_can_frame_return_ready(frame1); /* manual create, frame 2 (raw, pub, datatype 0x3ff, last frame, size 6, data "defgh" */ DI_CAN_SET_MSGTYPE(frame2->canid, DI_CAN_MSGTYPE_RAW); DI_CAN_SET_TRANSFERTYPE(frame2->canid, DI_CAN_TRANSFERTYPE_PUB); DI_CAN_SET_DATATYPE(frame2->canid, 0x3ff); DI_CAN_SET_LAST_FRAME(frame2->canid, DI_CAN_LAST_FRAME_TRUE); snprintf((char *)frame2->buf, sizeof(frame2->buf), "defgh"); frame2->size = 6; di_can_frame_return_ready(frame2); /* Reassemble frames into two di_can_msg */ ASSERT_EQ(0, di_can_reassemble(&ctx)); /* di_can_msg, check not NULL and msg1 != msg2 */ // TODO test also di_can_msg_get_ready ? msg1 = di_can_msg_get_ready(&ctx, DI_CAN_MSGTYPE_RAW); msg2 = di_can_msg_get_ready(&ctx, DI_CAN_MSGTYPE_RAW); ASSERT_NE((void *)NULL, msg1); ASSERT_NE((void *)NULL, msg2); ASSERT_NE(msg1, msg2); ASSERT_EQ(4U, msg1->size); ASSERT_EQ(6U, msg2->size); ASSERT_STREQ("abc", (const char *)msg1->msg); ASSERT_STREQ("defgh", (const char *)msg2->msg); } /** * Normal garbage collect test * This uses the single garbage collect run */ TEST(can_msg, gc_single) { struct di_can_msg *msg; /* Test garbage collection on m->timeout = 1000ms uptime = 2000ms */ test_can_msg_gc_put(&msg); msg->timeout = 1000; _di_time_uptime = 2000; di_can_msg_gc(&ctx, DI_CAN_GC_SINGLE); ASSERT_EQ(0U, msg->timeout); /* Test garbage collection on m->timeout = 1000ms uptime = 1000ms */ test_can_msg_gc_put(&msg); msg->timeout = 1000; _di_time_uptime = 1000; di_can_msg_gc(&ctx, DI_CAN_GC_SINGLE); ASSERT_EQ(0U, msg->timeout); /* Test garbage collection skip on m->timeout = 1000ms uptime = 750ms */ test_can_msg_gc_put(&msg); _di_time_uptime = 750; msg->timeout = 1000; di_can_msg_gc(&ctx, DI_CAN_GC_SINGLE); ASSERT_EQ(1000U, msg->timeout); /* Now garbage collect previous message */ _di_time_uptime = 1000; di_can_msg_gc(&ctx, DI_CAN_GC_SINGLE); msg = di_can_msg_get_used(&ctx, 0); ASSERT_EQ((void *)NULL, msg); } /** * Garbage collection skip and immediate macro test * * Put message * * Set garbage collect to skip * * Garbage collect */ TEST(can_msg, gc_timeout_skip_and_immediate) { struct di_buffer *b; struct di_can_msg *msg; /* Test if message GC is skipped */ test_can_msg_gc_put(&msg); msg->timeout = DI_CAN_TIMEOUT_GC_SKIP; b = msg->buf; _di_time_uptime = 1000; di_can_msg_gc(&ctx, DI_CAN_GC_ALL); msg = di_can_msg_get_used(&ctx, 0); ASSERT_NE((void *)NULL, msg); ASSERT_EQ(b, msg->buf); /* Test if message GC is immediate */ _di_time_uptime = 0; msg->timeout = DI_CAN_TIMEOUT_GC_IMMEDIATE; di_can_msg_gc(&ctx, DI_CAN_GC_ALL); msg = di_can_msg_get_used(&ctx, 0); ASSERT_EQ((void *)NULL, msg); } /** * Get unused messages after garbage collection * * Deplete buffers * * Set all buffers timeout to IMMEDIATE * * Fetch all buffers * * Return all buffers */ TEST(can_msg, gc_get_unused) { struct di_can_msg *msg; /* Deplete buffers */ for (size_t n = 0; n < DI_CAN_CFG_MSG_SIZE; n++) { msg = di_can_msg_get_unused(&ctx); ASSERT_NE((void *)NULL, msg); msg->timeout = DI_CAN_TIMEOUT_GC_IMMEDIATE; } /* Check depletion without gc by using internal function */ msg = _di_can_msg_get_unused(&ctx); ASSERT_EQ((void *)NULL, msg); /* Get single garbage collected msg */ msg = di_can_msg_get_unused(&ctx); ASSERT_NE((void *)NULL, msg); msg->timeout = DI_CAN_TIMEOUT_GC_IMMEDIATE; /* Let the garbage collect of all frames be performed */ di_can_msg_gc(&ctx, DI_CAN_GC_ALL); /* Check all free again, and return */ for (size_t n = 0; n < DI_CAN_CFG_MSG_SIZE; n++) { msg = di_can_msg_get_unused(&ctx); ASSERT_NE((void *)NULL, msg); di_can_msg_return_unused(&msg); ASSERT_EQ((void *)NULL, msg); } di_can_msg_gc(&ctx, DI_CAN_GC_ALL); } #endif