Feature/octetstring value and length api (#1264)
* feat: add octetstring_length_value_same function for length and content comparison * feat: add tests for characterstring UTF-8 validation * feat: add tests for octetstring_init_ascii_epics validation * feat: add test for bacnet_strdup string duplication validation * feat: add utf8_isvalid test cases * feat: add octet string value API for present-value octet-string value and length, and out-of-service properties
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
#include <stdint.h>
|
||||
#include <zephyr/ztest.h>
|
||||
#include <bacnet/bacstr.h>
|
||||
|
||||
@@ -265,6 +266,232 @@ static void testCharacterString(void)
|
||||
zassert_false(status, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test utf8_isvalid function
|
||||
*/
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(bacstr_tests, testUtf8IsValid)
|
||||
#else
|
||||
static void testUtf8IsValid(void)
|
||||
#endif
|
||||
{
|
||||
static const char ascii_value[] = "Joshua,Mary,Anna";
|
||||
static const char utf8_value[] = "Joshua😍Mary😍Anna";
|
||||
static const char valid_two_byte[] = { (char)0xC2, (char)0xA9 };
|
||||
static const char valid_three_byte[] = { (char)0xE2, (char)0x82,
|
||||
(char)0xAC };
|
||||
static const char valid_five_byte[] = { (char)0xF8, (char)0x88, (char)0x80,
|
||||
(char)0x80, (char)0x80 };
|
||||
static const char valid_six_byte[] = { (char)0xFC, (char)0x84, (char)0x80,
|
||||
(char)0x80, (char)0x80, (char)0x80 };
|
||||
static const char embedded_nul[] = { 'A', '\0', 'B' };
|
||||
static const char lone_continuation[] = { (char)0x80 };
|
||||
static const char truncated_multibyte[] = { 'A', (char)0xF0 };
|
||||
static const char invalid_continuation[] = { (char)0xC2, 'A' };
|
||||
static const char invalid_late_continuation[] = { (char)0xE2, (char)0x82,
|
||||
'A' };
|
||||
static const char overlong_two_byte[] = { (char)0xC0, (char)0x80 };
|
||||
static const char overlong_three_byte[] = { (char)0xE0, (char)0x80,
|
||||
(char)0x80 };
|
||||
static const char overlong_four_byte[] = { (char)0xF0, (char)0x80,
|
||||
(char)0x80, (char)0x80 };
|
||||
static const char overlong_five_byte[] = { (char)0xF8, (char)0x80,
|
||||
(char)0x80, (char)0x80,
|
||||
(char)0x80 };
|
||||
static const char overlong_six_byte[] = { (char)0xFC, (char)0x80,
|
||||
(char)0x80, (char)0x80,
|
||||
(char)0x80, (char)0x80 };
|
||||
static const char invalid_fe[] = { (char)0xFE, (char)0x80, (char)0x80,
|
||||
(char)0x80, (char)0x80, (char)0x80 };
|
||||
static const char invalid_ff[] = { (char)0xFF, (char)0x80, (char)0x80,
|
||||
(char)0x80, (char)0x80, (char)0x80 };
|
||||
|
||||
zassert_true(utf8_isvalid(NULL, 0), "Empty input should be valid");
|
||||
zassert_true(
|
||||
utf8_isvalid(ascii_value, strlen(ascii_value)),
|
||||
"ASCII input should be valid UTF-8");
|
||||
zassert_true(
|
||||
utf8_isvalid(valid_two_byte, sizeof(valid_two_byte)),
|
||||
"Valid 2-byte UTF-8 should pass validation");
|
||||
zassert_true(
|
||||
utf8_isvalid(valid_three_byte, sizeof(valid_three_byte)),
|
||||
"Valid 3-byte UTF-8 should pass validation");
|
||||
zassert_true(
|
||||
utf8_isvalid(utf8_value, strlen(utf8_value)),
|
||||
"Valid multibyte UTF-8 should pass validation");
|
||||
zassert_true(
|
||||
utf8_isvalid(valid_five_byte, sizeof(valid_five_byte)),
|
||||
"Valid 5-byte legacy UTF-8 should pass validation");
|
||||
zassert_true(
|
||||
utf8_isvalid(valid_six_byte, sizeof(valid_six_byte)),
|
||||
"Valid 6-byte legacy UTF-8 should pass validation");
|
||||
|
||||
zassert_false(utf8_isvalid(NULL, 1), "NULL input should be rejected");
|
||||
zassert_false(
|
||||
utf8_isvalid(embedded_nul, sizeof(embedded_nul)),
|
||||
"Embedded NUL should be rejected");
|
||||
zassert_false(
|
||||
utf8_isvalid(lone_continuation, sizeof(lone_continuation)),
|
||||
"Lone continuation byte should be rejected");
|
||||
zassert_false(
|
||||
utf8_isvalid(truncated_multibyte, sizeof(truncated_multibyte)),
|
||||
"Truncated multibyte sequence should be rejected");
|
||||
zassert_false(
|
||||
utf8_isvalid(invalid_continuation, sizeof(invalid_continuation)),
|
||||
"Invalid continuation byte should be rejected");
|
||||
zassert_false(
|
||||
utf8_isvalid(
|
||||
invalid_late_continuation, sizeof(invalid_late_continuation)),
|
||||
"Invalid later continuation byte should be rejected");
|
||||
zassert_false(
|
||||
utf8_isvalid(overlong_two_byte, sizeof(overlong_two_byte)),
|
||||
"Overlong 2-byte sequence should be rejected");
|
||||
zassert_false(
|
||||
utf8_isvalid(overlong_three_byte, sizeof(overlong_three_byte)),
|
||||
"Overlong 3-byte sequence should be rejected");
|
||||
zassert_false(
|
||||
utf8_isvalid(overlong_four_byte, sizeof(overlong_four_byte)),
|
||||
"Overlong 4-byte sequence should be rejected");
|
||||
zassert_false(
|
||||
utf8_isvalid(overlong_five_byte, sizeof(overlong_five_byte)),
|
||||
"Overlong 5-byte sequence should be rejected");
|
||||
zassert_false(
|
||||
utf8_isvalid(overlong_six_byte, sizeof(overlong_six_byte)),
|
||||
"Overlong 6-byte sequence should be rejected");
|
||||
zassert_false(
|
||||
utf8_isvalid(invalid_fe, sizeof(invalid_fe)),
|
||||
"0xFE lead byte should be rejected");
|
||||
zassert_false(
|
||||
utf8_isvalid(invalid_ff, sizeof(invalid_ff)),
|
||||
"0xFF lead byte should be rejected");
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(bacstr_tests, testCharacterStringUtf8Valid)
|
||||
#else
|
||||
static void testCharacterStringUtf8Valid(void)
|
||||
#endif
|
||||
{
|
||||
BACNET_CHARACTER_STRING bacnet_string = { 0 };
|
||||
const char *utf8_value = "Joshua😍Mary😍Anna";
|
||||
const char *ascii_value = "Joshua,Mary,Anna";
|
||||
bool status = false;
|
||||
|
||||
/* Test NULL pointer */
|
||||
status = characterstring_utf8_valid(NULL);
|
||||
zassert_false(status, "NULL pointer should return false");
|
||||
|
||||
/* Test non-UTF8 encoding - use CHARACTER_MS_DBCS (value 1) */
|
||||
status = characterstring_init_ansi(&bacnet_string, utf8_value);
|
||||
zassert_true(status, NULL);
|
||||
/* Verify it detects UTF-8 */
|
||||
zassert_equal(
|
||||
characterstring_encoding(&bacnet_string), CHARACTER_UTF8, NULL);
|
||||
/* Change encoding to a different non-UTF8 encoding (CHARACTER_MS_DBCS) */
|
||||
status = characterstring_set_encoding(&bacnet_string, CHARACTER_MS_DBCS);
|
||||
zassert_true(status, NULL);
|
||||
/* Now it should fail UTF-8 validation because encoding is not UTF-8 */
|
||||
status = characterstring_utf8_valid(&bacnet_string);
|
||||
zassert_false(status, "Non-UTF8 encoding should return false");
|
||||
|
||||
/* Test valid UTF-8 string */
|
||||
status = characterstring_init_ansi(&bacnet_string, utf8_value);
|
||||
zassert_true(status, NULL);
|
||||
zassert_equal(
|
||||
characterstring_encoding(&bacnet_string), CHARACTER_UTF8, NULL);
|
||||
status = characterstring_utf8_valid(&bacnet_string);
|
||||
zassert_true(status, "Valid UTF-8 string should return true");
|
||||
|
||||
/* Test empty UTF-8 string */
|
||||
status = characterstring_init(&bacnet_string, CHARACTER_UTF8, NULL, 0);
|
||||
zassert_true(status, NULL);
|
||||
status = characterstring_utf8_valid(&bacnet_string);
|
||||
zassert_true(status, "Empty UTF-8 string should return true");
|
||||
|
||||
/* Test valid UTF-8 string with plain ASCII */
|
||||
status = characterstring_init(
|
||||
&bacnet_string, CHARACTER_UTF8, ascii_value, strlen(ascii_value));
|
||||
zassert_true(status, NULL);
|
||||
status = characterstring_utf8_valid(&bacnet_string);
|
||||
zassert_true(status, "Valid ASCII-only UTF-8 string should return true");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test characterstring_utf8_strdup function
|
||||
*/
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(bacstr_tests, testCharacterStringUtf8Strdup)
|
||||
#else
|
||||
static void testCharacterStringUtf8Strdup(void)
|
||||
#endif
|
||||
{
|
||||
BACNET_CHARACTER_STRING bacnet_string = { 0 };
|
||||
const char *utf8_value = "Joshua😍Mary😍Anna";
|
||||
const char *ascii_value = "Joshua,Mary,Anna";
|
||||
char *dup_string = NULL;
|
||||
bool status = false;
|
||||
size_t length = 0;
|
||||
size_t i = 0;
|
||||
|
||||
/* Test NULL pointer */
|
||||
dup_string = characterstring_utf8_strdup(NULL);
|
||||
zassert_is_null(dup_string, "NULL pointer should return NULL");
|
||||
|
||||
/* Test non-UTF8 encoding - use CHARACTER_MS_DBCS (value 1) */
|
||||
status = characterstring_init_ansi(&bacnet_string, utf8_value);
|
||||
zassert_true(status, NULL);
|
||||
/* Verify it detects UTF-8 */
|
||||
zassert_equal(
|
||||
characterstring_encoding(&bacnet_string), CHARACTER_UTF8, NULL);
|
||||
/* Change encoding to a different non-UTF8 encoding (CHARACTER_MS_DBCS) */
|
||||
status = characterstring_set_encoding(&bacnet_string, CHARACTER_MS_DBCS);
|
||||
zassert_true(status, NULL);
|
||||
dup_string = characterstring_utf8_strdup(&bacnet_string);
|
||||
zassert_is_null(dup_string, "Non-UTF8 encoding should return NULL");
|
||||
|
||||
/* Test valid UTF-8 string duplication */
|
||||
status = characterstring_init_ansi(&bacnet_string, utf8_value);
|
||||
zassert_true(status, NULL);
|
||||
zassert_equal(
|
||||
characterstring_encoding(&bacnet_string), CHARACTER_UTF8, NULL);
|
||||
dup_string = characterstring_utf8_strdup(&bacnet_string);
|
||||
zassert_not_null(dup_string, "Valid UTF-8 string should return non-NULL");
|
||||
length = characterstring_length(&bacnet_string);
|
||||
/* Verify the duplicated string has correct content */
|
||||
for (i = 0; i < length; i++) {
|
||||
zassert_equal(
|
||||
dup_string[i], characterstring_value(&bacnet_string)[i],
|
||||
"Duplicated strings should match at byte %u", i);
|
||||
}
|
||||
/* Verify NUL-termination */
|
||||
zassert_equal(dup_string[length], 0, "String should be NUL-terminated");
|
||||
free(dup_string);
|
||||
dup_string = NULL;
|
||||
|
||||
/* Test empty UTF-8 string duplication */
|
||||
status = characterstring_init(&bacnet_string, CHARACTER_UTF8, NULL, 0);
|
||||
zassert_true(status, NULL);
|
||||
dup_string = characterstring_utf8_strdup(&bacnet_string);
|
||||
zassert_not_null(dup_string, "Empty UTF-8 string should return non-NULL");
|
||||
zassert_equal(dup_string[0], 0, "Empty string should be NUL-terminated");
|
||||
free(dup_string);
|
||||
dup_string = NULL;
|
||||
|
||||
/* Test valid UTF-8 string with plain ASCII */
|
||||
status = characterstring_init(
|
||||
&bacnet_string, CHARACTER_UTF8, ascii_value, strlen(ascii_value));
|
||||
zassert_true(status, NULL);
|
||||
dup_string = characterstring_utf8_strdup(&bacnet_string);
|
||||
zassert_not_null(dup_string, "ASCII UTF-8 string should return non-NULL");
|
||||
length = characterstring_length(&bacnet_string);
|
||||
zassert_equal(
|
||||
strncmp(dup_string, ascii_value, length), 0,
|
||||
"Duplicated ASCII-UTF8 string should match original");
|
||||
zassert_equal(dup_string[length], 0, "String should be NUL-terminated");
|
||||
free(dup_string);
|
||||
dup_string = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test encode/decode API for octet strings
|
||||
*/
|
||||
@@ -409,6 +636,87 @@ static void testOctetString(void)
|
||||
zassert_true(status, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test octetstring_init_ascii_epics API
|
||||
*/
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(bacstr_tests, test_octetstring_init_ascii_epics)
|
||||
#else
|
||||
static void test_octetstring_init_ascii_epics(void)
|
||||
#endif
|
||||
{
|
||||
BACNET_OCTET_STRING bacnet_string;
|
||||
const char *epics_valid_hex = "X'1234567890ABCDEF'";
|
||||
const char *epics_valid_hex_with_colons = "X'12:34:56:78:90:AB:CD:EF'";
|
||||
const char *epics_invalid_no_prefix = "1234567890ABCDEF";
|
||||
const char *epics_invalid_wrong_prefix = "H'1234567890ABCDEF'";
|
||||
const char *epics_invalid_odd_single = "X'1";
|
||||
char epics_too_long[MAX_APDU + MAX_APDU] = "";
|
||||
bool status = false;
|
||||
size_t length = 0;
|
||||
size_t test_length = 0;
|
||||
uint8_t *value = NULL;
|
||||
|
||||
/* test valid EPICS format with hex string */
|
||||
status = octetstring_init_ascii_epics(&bacnet_string, epics_valid_hex);
|
||||
zassert_true(status, "Valid EPICS hex should return true");
|
||||
length = octetstring_length(&bacnet_string);
|
||||
test_length = strlen(epics_valid_hex) - 3; /* subtract X'' */
|
||||
test_length = test_length / 2; /* convert hex chars to byte count */
|
||||
zassert_equal(length, test_length, "Length should be 8 bytes");
|
||||
value = octetstring_value(&bacnet_string);
|
||||
zassert_equal(value[0], 0x12, "First byte should be 0x12");
|
||||
zassert_equal(value[1], 0x34, "Second byte should be 0x34");
|
||||
zassert_equal(value[7], 0xEF, "Last byte should be 0xEF");
|
||||
|
||||
/* test valid EPICS format with colons as separators */
|
||||
status = octetstring_init_ascii_epics(
|
||||
&bacnet_string, epics_valid_hex_with_colons);
|
||||
zassert_true(status, "Valid EPICS hex with colons should return true");
|
||||
length = octetstring_length(&bacnet_string);
|
||||
zassert_equal(length, 8, "Length should be 8 bytes");
|
||||
value = octetstring_value(&bacnet_string);
|
||||
zassert_equal(value[0], 0x12, "First byte should be 0x12");
|
||||
zassert_equal(value[1], 0x34, "Second byte should be 0x34");
|
||||
|
||||
/* test invalid - NULL octet string pointer */
|
||||
status = octetstring_init_ascii_epics(NULL, epics_valid_hex);
|
||||
zassert_false(status, "NULL octet_string pointer should return false");
|
||||
|
||||
/* test invalid - NULL arg pointer */
|
||||
status = octetstring_init_ascii_epics(&bacnet_string, NULL);
|
||||
zassert_false(status, "NULL arg pointer should return false");
|
||||
|
||||
/* test invalid - both NULL */
|
||||
status = octetstring_init_ascii_epics(NULL, NULL);
|
||||
zassert_false(status, "Both NULL pointers should return false");
|
||||
|
||||
/* test invalid - no X' prefix */
|
||||
status =
|
||||
octetstring_init_ascii_epics(&bacnet_string, epics_invalid_no_prefix);
|
||||
zassert_false(status, "String without X' prefix should return false");
|
||||
|
||||
/* test invalid - wrong prefix */
|
||||
status = octetstring_init_ascii_epics(
|
||||
&bacnet_string, epics_invalid_wrong_prefix);
|
||||
zassert_false(status, "String with H' prefix should return false");
|
||||
|
||||
/* test invalid - odd number of hex digits */
|
||||
status =
|
||||
octetstring_init_ascii_epics(&bacnet_string, epics_invalid_odd_single);
|
||||
zassert_false(status, "Single hex digit without pair should return false");
|
||||
|
||||
/* test invalid - string too long */
|
||||
memset(
|
||||
epics_too_long, 'F',
|
||||
sizeof(epics_too_long) - 1); /* -1 for null terminator */
|
||||
epics_too_long[0] = 'X';
|
||||
epics_too_long[1] = '\'';
|
||||
epics_too_long[sizeof(epics_too_long) - 1] = 0; /* null terminate */
|
||||
status = octetstring_init_ascii_epics(&bacnet_string, epics_too_long);
|
||||
zassert_false(status, "String exceeding capacity should return false");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test encode/decode API for bacnet_stricmp
|
||||
*/
|
||||
@@ -828,6 +1136,73 @@ static void test_bacnet_snprintf(void)
|
||||
zassert_equal(null_len, test_null_len, "null_len=%d", null_len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Test bacnet_strdup string duplication
|
||||
*/
|
||||
#if defined(CONFIG_ZTEST_NEW_API)
|
||||
ZTEST(bacstr_tests, test_bacnet_strdup)
|
||||
#else
|
||||
static void test_bacnet_strdup(void)
|
||||
#endif
|
||||
{
|
||||
const char *original = "Test String";
|
||||
const char *empty_string = "";
|
||||
const char *long_string =
|
||||
"This is a longer test string with multiple words";
|
||||
char *dup_string = NULL;
|
||||
|
||||
/* Test NULL pointer */
|
||||
dup_string = bacnet_strdup(NULL);
|
||||
zassert_is_null(dup_string, "NULL input should return NULL");
|
||||
|
||||
/* Test empty string */
|
||||
dup_string = bacnet_strdup(empty_string);
|
||||
zassert_not_null(dup_string, "Empty string should allocate memory");
|
||||
zassert_equal(
|
||||
strlen(dup_string), 0, "Duplicated empty string should have length 0");
|
||||
zassert_equal(
|
||||
bacnet_strcmp(dup_string, empty_string), 0,
|
||||
"Duplicated string should match original");
|
||||
free(dup_string);
|
||||
|
||||
/* Test normal string */
|
||||
dup_string = bacnet_strdup(original);
|
||||
zassert_not_null(dup_string, "Normal string should allocate memory");
|
||||
zassert_equal(
|
||||
bacnet_strcmp(dup_string, original), 0,
|
||||
"Duplicated string should match original");
|
||||
/* Verify different memory addresses */
|
||||
zassert_not_equal(
|
||||
(uintptr_t)dup_string, (uintptr_t)original,
|
||||
"Duplicated string should have different address");
|
||||
/* Verify string length is preserved */
|
||||
zassert_equal(
|
||||
strlen(dup_string), strlen(original),
|
||||
"String length should be preserved");
|
||||
free(dup_string);
|
||||
|
||||
/* Test longer string */
|
||||
dup_string = bacnet_strdup(long_string);
|
||||
zassert_not_null(dup_string, "Longer string should allocate memory");
|
||||
zassert_equal(
|
||||
bacnet_strcmp(dup_string, long_string), 0,
|
||||
"Duplicated longer string should match original");
|
||||
zassert_equal(
|
||||
strlen(dup_string), strlen(long_string),
|
||||
"Longer string length should be preserved");
|
||||
free(dup_string);
|
||||
|
||||
/* Test string with special characters */
|
||||
const char *special_chars = "Hello\tWorld\nTest!";
|
||||
dup_string = bacnet_strdup(special_chars);
|
||||
zassert_not_null(
|
||||
dup_string, "String with special chars should allocate memory");
|
||||
zassert_equal(
|
||||
bacnet_strcmp(dup_string, special_chars), 0,
|
||||
"Special characters should be preserved");
|
||||
free(dup_string);
|
||||
}
|
||||
|
||||
/**
|
||||
* @}
|
||||
*/
|
||||
@@ -839,7 +1214,11 @@ void test_main(void)
|
||||
{
|
||||
ztest_test_suite(
|
||||
bacstr_tests, ztest_unit_test(testBitString),
|
||||
ztest_unit_test(testCharacterString), ztest_unit_test(testOctetString),
|
||||
ztest_unit_test(testCharacterString), ztest_unit_test(testUtf8IsValid),
|
||||
ztest_unit_test(testCharacterStringUtf8Valid),
|
||||
ztest_unit_test(testCharacterStringUtf8Strdup),
|
||||
ztest_unit_test(testOctetString),
|
||||
ztest_unit_test(test_octetstring_init_ascii_epics),
|
||||
ztest_unit_test(test_bacnet_stricmp),
|
||||
ztest_unit_test(test_bacnet_strnicmp),
|
||||
ztest_unit_test(test_bacnet_strnlen),
|
||||
@@ -847,7 +1226,8 @@ void test_main(void)
|
||||
ztest_unit_test(test_bacnet_string_to_x),
|
||||
ztest_unit_test(test_bacnet_string_trim),
|
||||
ztest_unit_test(test_bacnet_stptok),
|
||||
ztest_unit_test(test_bacnet_snprintf));
|
||||
ztest_unit_test(test_bacnet_snprintf),
|
||||
ztest_unit_test(test_bacnet_strdup));
|
||||
ztest_run_test_suite(bacstr_tests);
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user