feat: implement cel_crsf_param_parse
Parse PARAM_ENTRY payload into cel_crsf_param struct. Handles TEXT_SELECT with options string and UINT8/INT8 with min/max/default/ value fields. Respects hidden flag (bit 7 of type byte). Truncates name and options to buffer limits.
This commit is contained in:
@@ -55,6 +55,14 @@ target_compile_features(test_crsf_telemetry PRIVATE c_std_23)
|
||||
add_test(NAME test_crsf_telemetry COMMAND test_crsf_telemetry)
|
||||
list(APPEND TEST_TARGETS test_crsf_telemetry)
|
||||
|
||||
# CRSF param tests — pure functions (parse), no mock needed
|
||||
add_executable(test_crsf_param test_crsf_param.c)
|
||||
target_include_directories(test_crsf_param PRIVATE "${CMAKE_SOURCE_DIR}")
|
||||
target_link_libraries(test_crsf_param PRIVATE celrs_crsf Unity::Unity)
|
||||
target_compile_features(test_crsf_param PRIVATE c_std_23)
|
||||
add_test(NAME test_crsf_param COMMAND test_crsf_param)
|
||||
list(APPEND TEST_TARGETS test_crsf_param)
|
||||
|
||||
# Serial tests — mocks the platform backend (serial_internal.h)
|
||||
add_executable(test_serial test_serial.c)
|
||||
target_include_directories(test_serial PRIVATE "${CMAKE_SOURCE_DIR}")
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
#include "unity.h"
|
||||
#include "celrs/crsf_param.h"
|
||||
#include <string.h>
|
||||
|
||||
void setUp(void) {}
|
||||
void tearDown(void) {}
|
||||
|
||||
/* cel_crsf_param_parse tests */
|
||||
|
||||
void test_param_parse_null_args(void) {
|
||||
uint8_t payload[16] = {0};
|
||||
TEST_ASSERT_EQUAL_INT(-1, cel_crsf_param_parse(NULL, payload, sizeof(payload)));
|
||||
TEST_ASSERT_EQUAL_INT(-1, cel_crsf_param_parse(NULL, NULL, 0));
|
||||
}
|
||||
|
||||
void test_param_parse_too_short(void) {
|
||||
cel_crsf_param param;
|
||||
uint8_t payload[5] = {0};
|
||||
TEST_ASSERT_EQUAL_INT(-1, cel_crsf_param_parse(¶m, payload, sizeof(payload)));
|
||||
}
|
||||
|
||||
void test_param_parse_text_select(void) {
|
||||
cel_crsf_param param;
|
||||
/* dest=0x10, src=0xEE, index=5, chunks=0, parent=0, type=TEXT_SELECT */
|
||||
/* name="TX Power", options="10 mW;25 mW;100 mW;500 mW;1000 mW", value=2, min=0, max=4, default=0 */
|
||||
uint8_t payload[] = {
|
||||
0x10, /* dest */
|
||||
0xEE, /* src */
|
||||
0x05, /* index */
|
||||
0x00, /* chunks_remaining */
|
||||
0x00, /* parent */
|
||||
0x09, /* type = CEL_PARAM_TEXT_SELECT */
|
||||
'T', 'X', ' ', 'P', 'o', 'w', 'e', 'r', '\0', /* name */
|
||||
'1', '0', ' ', 'm', 'W', ';', /* options */
|
||||
'2', '5', ' ', 'm', 'W', ';',
|
||||
'1', '0', '0', ' ', 'm', 'W', ';',
|
||||
'5', '0', '0', ' ', 'm', 'W', ';',
|
||||
'1', '0', '0', '0', ' ', 'm', 'W', '\0', /* end of options */
|
||||
0x02, /* value */
|
||||
0x00, /* min */
|
||||
0x04, /* max */
|
||||
0x00, /* default */
|
||||
};
|
||||
TEST_ASSERT_EQUAL_INT(0, cel_crsf_param_parse(¶m, payload, sizeof(payload)));
|
||||
TEST_ASSERT_EQUAL_UINT8(5, param.index);
|
||||
TEST_ASSERT_EQUAL_UINT8(CEL_PARAM_TEXT_SELECT, param.type);
|
||||
TEST_ASSERT_EQUAL_UINT8(0, param.hidden);
|
||||
TEST_ASSERT_EQUAL_STRING("TX Power", param.name);
|
||||
TEST_ASSERT_EQUAL_STRING("10 mW;25 mW;100 mW;500 mW;1000 mW", param.options);
|
||||
TEST_ASSERT_EQUAL_UINT8(2, param.value);
|
||||
TEST_ASSERT_EQUAL_UINT8(0, param.min_val);
|
||||
TEST_ASSERT_EQUAL_UINT8(4, param.max_val);
|
||||
TEST_ASSERT_EQUAL_UINT8(0, param.default_val);
|
||||
}
|
||||
|
||||
void test_param_parse_hidden_flag(void) {
|
||||
cel_crsf_param param;
|
||||
uint8_t payload[] = {
|
||||
0x10, 0xEE, 0x00, 0x00, 0x00,
|
||||
0x88, /* type with hidden bit set (0x80 | 0x08) */
|
||||
'H', 'i', 'd', 'd', 'e', 'n', '\0',
|
||||
'A', ';', 'B', '\0',
|
||||
0x00, 0x00, 0x01, 0x00,
|
||||
};
|
||||
TEST_ASSERT_EQUAL_INT(0, cel_crsf_param_parse(¶m, payload, sizeof(payload)));
|
||||
TEST_ASSERT_EQUAL_UINT8(CEL_PARAM_FLOAT, param.type);
|
||||
TEST_ASSERT_EQUAL_UINT8(1, param.hidden);
|
||||
}
|
||||
|
||||
void test_param_parse_uint8_type(void) {
|
||||
cel_crsf_param param;
|
||||
uint8_t payload[] = {
|
||||
0x10, 0xEE, 0x10, 0x00, 0x00,
|
||||
0x00, /* type = CEL_PARAM_UINT8 */
|
||||
'V', 'a', 'l', '\0', /* name */
|
||||
0x00, /* min */
|
||||
0xFF, /* max */
|
||||
0x80, /* default */
|
||||
0x42, /* value */
|
||||
};
|
||||
TEST_ASSERT_EQUAL_INT(0, cel_crsf_param_parse(¶m, payload, sizeof(payload)));
|
||||
TEST_ASSERT_EQUAL_UINT8(0x10, param.index);
|
||||
TEST_ASSERT_EQUAL_UINT8(CEL_PARAM_UINT8, param.type);
|
||||
TEST_ASSERT_EQUAL_STRING("Val", param.name);
|
||||
TEST_ASSERT_EQUAL_UINT8(0x42, param.value);
|
||||
TEST_ASSERT_EQUAL_UINT8(0x00, param.min_val);
|
||||
TEST_ASSERT_EQUAL_UINT8(0xFF, param.max_val);
|
||||
TEST_ASSERT_EQUAL_UINT8(0x80, param.default_val);
|
||||
}
|
||||
|
||||
void test_param_parse_int8_type(void) {
|
||||
cel_crsf_param param;
|
||||
uint8_t payload[] = {
|
||||
0x10, 0xEE, 0x01, 0x00, 0x00,
|
||||
0x01, /* type = CEL_PARAM_INT8 */
|
||||
'S', '\0',
|
||||
0x80, /* min (-128) */
|
||||
0x7F, /* max (127) */
|
||||
0x00, /* default */
|
||||
0x10, /* value */
|
||||
};
|
||||
TEST_ASSERT_EQUAL_INT(0, cel_crsf_param_parse(¶m, payload, sizeof(payload)));
|
||||
TEST_ASSERT_EQUAL_UINT8(CEL_PARAM_INT8, param.type);
|
||||
TEST_ASSERT_EQUAL_UINT8(0x10, param.value);
|
||||
}
|
||||
|
||||
void test_param_parse_folder(void) {
|
||||
cel_crsf_param param;
|
||||
uint8_t payload[] = {
|
||||
0x10, 0xEE, 0xFF, 0x00, 0x00,
|
||||
0x0B, /* type = CEL_PARAM_FOLDER */
|
||||
'F', 'o', 'l', 'd', 'e', 'r', '\0',
|
||||
};
|
||||
TEST_ASSERT_EQUAL_INT(0, cel_crsf_param_parse(¶m, payload, sizeof(payload)));
|
||||
TEST_ASSERT_EQUAL_UINT8(CEL_PARAM_FOLDER, param.type);
|
||||
TEST_ASSERT_EQUAL_STRING("Folder", param.name);
|
||||
}
|
||||
|
||||
void test_param_parse_name_truncation(void) {
|
||||
cel_crsf_param param;
|
||||
/* Name longer than 63 chars (should be truncated) */
|
||||
uint8_t payload[128] = {0};
|
||||
payload[0] = 0x10; /* dest */
|
||||
payload[1] = 0xEE; /* src */
|
||||
payload[2] = 0x00; /* index */
|
||||
payload[3] = 0x00; /* chunks */
|
||||
payload[4] = 0x00; /* parent */
|
||||
payload[5] = 0x0B; /* type = FOLDER */
|
||||
/* Fill name with 70 'A' chars */
|
||||
for (int i = 0; i < 70; i++) payload[6 + i] = 'A';
|
||||
payload[6 + 70] = '\0';
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, cel_crsf_param_parse(¶m, payload, sizeof(payload)));
|
||||
TEST_ASSERT_EQUAL_UINT8(63, strlen(param.name)); /* truncated to 63 */
|
||||
}
|
||||
|
||||
void test_param_parse_options_truncation(void) {
|
||||
cel_crsf_param param;
|
||||
/* Options longer than 255 chars (should be truncated) */
|
||||
uint8_t payload[400] = {0};
|
||||
payload[0] = 0x10; /* dest */
|
||||
payload[1] = 0xEE; /* src */
|
||||
payload[2] = 0x00; /* index */
|
||||
payload[3] = 0x00; /* chunks */
|
||||
payload[4] = 0x00; /* parent */
|
||||
payload[5] = 0x09; /* type = TEXT_SELECT */
|
||||
strcpy((char*)(payload + 6), "Name");
|
||||
payload[6 + 4] = '\0';
|
||||
/* Fill options with 300 'O' chars */
|
||||
size_t opts_start = 6 + 5; /* after name */
|
||||
for (int i = 0; i < 299; i++) payload[opts_start + i] = 'O';
|
||||
payload[opts_start + 299] = '\0';
|
||||
/* Add type-specific data after options */
|
||||
size_t after_opts = opts_start + 300;
|
||||
payload[after_opts] = 0x00; /* value */
|
||||
payload[after_opts + 1] = 0x00; /* min */
|
||||
payload[after_opts + 2] = 0x00; /* max */
|
||||
payload[after_opts + 3] = 0x00; /* default */
|
||||
|
||||
TEST_ASSERT_EQUAL_INT(0, cel_crsf_param_parse(¶m, payload, sizeof(payload)));
|
||||
TEST_ASSERT_EQUAL_UINT8(255, strlen(param.options)); /* truncated to 255 */
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
UNITY_BEGIN();
|
||||
RUN_TEST(test_param_parse_null_args);
|
||||
RUN_TEST(test_param_parse_too_short);
|
||||
RUN_TEST(test_param_parse_text_select);
|
||||
RUN_TEST(test_param_parse_hidden_flag);
|
||||
RUN_TEST(test_param_parse_uint8_type);
|
||||
RUN_TEST(test_param_parse_int8_type);
|
||||
RUN_TEST(test_param_parse_folder);
|
||||
RUN_TEST(test_param_parse_name_truncation);
|
||||
RUN_TEST(test_param_parse_options_truncation);
|
||||
return UNITY_END();
|
||||
}
|
||||
Reference in New Issue
Block a user