src.dualinventive.com/dinet/libdi/tests/can_msg.cpp

378 lines
11 KiB
C++

/**
* @file tests/can_msg.cpp
* @brief CAN message unit test
* @date Sep 30, 2015
* @author jjacobs
* @copyright 2015 Dual Inventive Technology Centre B.V.
*
* CAN Message unit test
*/
#include <stdio.h>
#include "tests/can.h"
#include <gtest/gtest.h>
#include <di/time.h>
#include <di/buffer.h>
#include <di/can/msg.h>
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);
/* di_can_msg_gc needs time */
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(&ctx, NULL, 0, NULL, 0);
di_can_msg_init_array(&ctx, can_msg_list, DI_CAN_CFG_MSG_SIZE, NULL, DI_CAN_CFG_MSG_SIZE);
di_can_msg_init_array(&ctx, 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_alloc_reassemble(&ctx);
ASSERT_EQ((void *)NULL, msg);
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_ready_type(&ctx, DI_CAN_MSGTYPE_RAW);
ASSERT_EQ((void *)NULL, msg);
msg = di_can_msg_get_ready_type(&ctx, DI_CAN_MSGTYPE_RPC);
ASSERT_EQ((void *)NULL, msg);
msg = di_can_msg_get_ready_canid(&ctx, 0x00000000);
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(&ctx, 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(&ctx, msg->ctx);
}
/* 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 = strlen(text) + 1;
di_buffer_memcpy(msg->buf, 0, text, strlen(text) + 1);
_di_can_msg_reset(&ctx, msg);
ASSERT_EQ(0U, msg->size);
ASSERT_STREQ(text, (const char *)msg->buf->data);
struct di_can_msg *msg_copy = msg; /* Copy the pointer of the msg before we free */
di_can_msg_free(&msg);
/* Check if still "Hello World!" is set and the fields are reset correctly */
ASSERT_EQ(0U, msg_copy->size);
ASSERT_EQ(&ctx, msg_copy->ctx); /* Even after a reset the can context backref is attached to the message */
ASSERT_STREQ(text, (const char *)msg_copy->buf->data);
}
/**
* Check if NULL message return will not crash
*/
TEST(can_msg, return_null) {
struct di_can_msg *msg = NULL;
/* NULL */
di_can_msg_set_ready((struct di_can_msg **)NULL);
di_can_msg_free((struct di_can_msg **)NULL);
/* msg = NULL */
di_can_msg_set_ready(&msg);
di_can_msg_free(&msg);
}
/** Test if ctx = NULL argument is correctly handled */
TEST(can_msg, get_invalid) {
ASSERT_EQ((void *)NULL, di_can_msg_alloc(NULL));
ASSERT_EQ((void *)NULL, di_can_msg_alloc_reassemble(NULL));
ASSERT_EQ((void *)NULL, di_can_msg_search(NULL, 0));
ASSERT_EQ((void *)NULL, di_can_msg_get_ready_type(NULL, DI_CAN_MSGTYPE_RAW));
ASSERT_EQ((void *)NULL, di_can_msg_get_ready_type(NULL, DI_CAN_MSGTYPE_RPC));
ASSERT_EQ((void *)NULL, di_can_msg_get_ready_canid(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;
/* Message with CAN id to search for */
DI_CAN_SET_MSGTYPE(canid, DI_CAN_MSGTYPE_RPC);
DI_CAN_SET_DATATYPE(canid, DI_RPC_TYPE_LOG_DEBUG);
DI_CAN_SET_TRANSACTION(canid, 2);
/* Doesn't exist */
msg = di_can_msg_search(&ctx, canid);
ASSERT_EQ((void *)NULL, msg);
/* Allocate message (normally done by di_can_reassemble) and write canid */
msg = di_can_msg_alloc_reassemble(&ctx);
ASSERT_NE((void *)NULL, msg);
msg->canid = canid;
/* Check if it is used */
msg = di_can_msg_search(&ctx, canid);
ASSERT_NE((void *)NULL, msg);
ASSERT_EQ(canid, msg->canid);
/* Doesn't exist */
msg = di_can_msg_search(&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_search(&ctx, canid);
ASSERT_EQ((void *)NULL, msg);
// reset
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_search(&ctx, canid);
ASSERT_EQ((void *)NULL, msg);
// reset
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_search(&ctx, canid);
ASSERT_EQ((void *)NULL, msg);
// reset
DI_CAN_SET_TRANSACTION(canid, 2);
/* Return message, because at this point
* msg->state == DI_CAN_MSG_STATE_REASSEMBLE
*/
di_can_msg_free(&msg);
/* Check if removed from ctx */
msg = di_can_msg_search(&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);
}
/**
* Bug: 2 frames should result in two messages
* the frame -> msg reassambler did write them into one di_can_msg.
*/
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(&ctx, can_msg_list, DI_CAN_CFG_MSG_SIZE, DI_CAN_CFG_MSG_DATA_SIZE);
ASSERT_EQ(0, di_can_init(&ctx));
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_PUBLISH);
DI_CAN_SET_DATATYPE(frame1->canid, 0x3ff);
DI_CAN_SET_LAST_FRAME(frame1->canid, DI_CAN_LAST_FRAME_TRUE);
frame1->size = 8;
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_PUBLISH);
DI_CAN_SET_DATATYPE(frame2->canid, 0x3ff);
DI_CAN_SET_LAST_FRAME(frame2->canid, DI_CAN_LAST_FRAME_TRUE);
frame2->size = 8;
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 */
msg1 = di_can_msg_get_ready_type(&ctx, DI_CAN_MSGTYPE_RAW);
msg2 = di_can_msg_get_ready_type(&ctx, DI_CAN_MSGTYPE_RAW);
ASSERT_NE((void *)NULL, msg1);
ASSERT_NE((void *)NULL, msg2);
ASSERT_NE(msg1, msg2);
/* SFT message have always a payload size of 0 bytes */
ASSERT_EQ(0U, msg1->size);
ASSERT_EQ(0U, msg2->size);
ASSERT_EQ((void *)NULL, (const char *)msg1->msg);
ASSERT_EQ((void *)NULL, (const char *)msg2->msg);
}
TEST(can_msg, memcpy) {
const char *memcpybuf1 = "Hello World!";
const char *memcpybuf2 = "ABCDEFG";
const char *memcpybuf3 = "ABC";
struct di_can_msg *msg = di_can_msg_alloc(&ctx);
// Check message is correctly reset from pool allocator
ASSERT_NE((void *)NULL, msg);
ASSERT_EQ(0, msg->size);
ASSERT_EQ((void *)NULL, msg->msg);
// Data > (1KByte (DI_CAN_FRAMING_SIZE_MAX) + 1)
uint8_t buf[DI_CAN_FRAMING_SIZE_MAX + 1];
di_can_msg_memcpy(msg, buf, DI_CAN_FRAMING_SIZE_MAX + 1);
// Data > 8 bytes (includes \0 char)
di_can_msg_memcpy(msg, (const uint8_t *)memcpybuf1, strlen(memcpybuf1) + 1);
ASSERT_STREQ("Hello World!", (const char *)(msg->buf->data));
// Data == 8 bytes (includes \0 char)
di_can_msg_memcpy(msg, (const uint8_t *)memcpybuf2, strlen(memcpybuf2) + 1);
ASSERT_STREQ("ABCDEFG", (const char *)msg->buf->data);
// Data == 4 bytes (include \0 char)
di_buffer_flush(msg->buf);
di_can_msg_memcpy(msg, (const uint8_t *)memcpybuf3, strlen(memcpybuf3) + 1);
ASSERT_STREQ("ABC", (const char *)msg->buf->data);
di_can_msg_free(&msg);
}