#include "unity/unity.h" #include #include #include #include #include void setUp(void) { xmlInitParser(); } void tearDown(void) { xmlCleanupParser(); } /* Helper to compare output buffer to expected C string. Ensures null-termination locally without relying on function behavior. */ static void assert_output_equals(const unsigned char *out, int outlen, const char *expected) { size_t explen = strlen(expected); TEST_ASSERT_EQUAL_UINT32_MESSAGE((uint32_t)explen, (uint32_t)outlen, "Output length mismatch"); char *tmp = (char *)malloc(outlen + 1); TEST_ASSERT_NOT_NULL(tmp); memcpy(tmp, out, outlen); tmp[outlen] = '\0'; TEST_ASSERT_EQUAL_STRING(expected, tmp); free(tmp); } /* 1) Null argument checks */ void test_htmlEncodeEntities_null_args(void) { unsigned char outbuf[32]; int outlen = (int)sizeof(outbuf); const unsigned char inbuf[] = "test"; int inlen = 4; int rc; rc = htmlEncodeEntities(NULL, &outlen, inbuf, &inlen, 0); TEST_ASSERT_EQUAL_INT(-1, rc); outlen = (int)sizeof(outbuf); inlen = 4; rc = htmlEncodeEntities(outbuf, NULL, inbuf, &inlen, 0); TEST_ASSERT_EQUAL_INT(-1, rc); outlen = (int)sizeof(outbuf); inlen = 4; rc = htmlEncodeEntities(outbuf, &outlen, NULL, &inlen, 0); TEST_ASSERT_EQUAL_INT(-1, rc); outlen = (int)sizeof(outbuf); inlen = 4; rc = htmlEncodeEntities(outbuf, &outlen, inbuf, NULL, 0); TEST_ASSERT_EQUAL_INT(-1, rc); } /* 2) ASCII passthrough without special chars */ void test_htmlEncodeEntities_ascii_passthrough(void) { const char *in = "Hello world!"; int inlen = (int)strlen(in); unsigned char outbuf[128]; int outlen = (int)sizeof(outbuf); int rc = htmlEncodeEntities(outbuf, &outlen, (const unsigned char*)in, &inlen, 0); TEST_ASSERT_EQUAL_INT(0, rc); TEST_ASSERT_EQUAL_INT((int)strlen(in), outlen); TEST_ASSERT_EQUAL_INT((int)strlen(in), inlen); assert_output_equals(outbuf, outlen, in); } /* 3) Special ASCII chars &, <, > */ void test_htmlEncodeEntities_special_ascii_entities(void) { const char *in = "&<>"; int inlen = (int)strlen(in); unsigned char outbuf[64]; int outlen = (int)sizeof(outbuf); int rc = htmlEncodeEntities(outbuf, &outlen, (const unsigned char*)in, &inlen, 0); TEST_ASSERT_EQUAL_INT(0, rc); assert_output_equals(outbuf, outlen, "&<>"); TEST_ASSERT_EQUAL_INT(3, inlen); } /* 4) Quotes handling with quoteChar = '\"' */ void test_htmlEncodeEntities_double_quote_encoded(void) { const char inbytes[] = {'\'', '\"'}; int inlen = 2; unsigned char outbuf[64]; int outlen = (int)sizeof(outbuf); int rc = htmlEncodeEntities(outbuf, &outlen, (const unsigned char*)inbytes, &inlen, '\"'); TEST_ASSERT_EQUAL_INT(0, rc); assert_output_equals(outbuf, outlen, "'""); TEST_ASSERT_EQUAL_INT(2, inlen); } /* 5) Single quote encoded when quoteChar = '\'' (either ' or numeric) */ void test_htmlEncodeEntities_single_quote_encoded(void) { const char inbytes[] = {'\''}; int inlen = 1; unsigned char outbuf[64]; int outlen = (int)sizeof(outbuf); int rc = htmlEncodeEntities(outbuf, &outlen, (const unsigned char*)inbytes, &inlen, '\''); TEST_ASSERT_EQUAL_INT(0, rc); /* Convert output to C string */ char tmp[64]; memcpy(tmp, outbuf, outlen); tmp[outlen] = '\0'; /* Accept either ' or ' depending on entity table */ int is_apos = (strcmp(tmp, "'") == 0); int is_num = (strcmp(tmp, "'") == 0); TEST_ASSERT_TRUE_MESSAGE(is_apos || is_num, "Expected ' or ' for single quote"); TEST_ASSERT_EQUAL_INT(1, inlen); } /* 6) Non-ASCII with named entity: © -> © */ void test_htmlEncodeEntities_named_entity_copyright(void) { const unsigned char inbytes[] = {0xC2, 0xA9}; /* U+00A9 © */ int inlen = 2; unsigned char outbuf[64]; int outlen = (int)sizeof(outbuf); int rc = htmlEncodeEntities(outbuf, &outlen, inbytes, &inlen, 0); TEST_ASSERT_EQUAL_INT(0, rc); assert_output_equals(outbuf, outlen, "©"); TEST_ASSERT_EQUAL_INT(2, inlen); } /* 7) Non-ASCII without named entity: 🙂 -> 🙂 */ void test_htmlEncodeEntities_numeric_entity_smiley(void) { const unsigned char inbytes[] = {0xF0, 0x9F, 0x99, 0x82}; /* U+1F642 🙂 */ int inlen = 4; unsigned char outbuf[64]; int outlen = (int)sizeof(outbuf); int rc = htmlEncodeEntities(outbuf, &outlen, inbytes, &inlen, 0); TEST_ASSERT_EQUAL_INT(0, rc); assert_output_equals(outbuf, outlen, "🙂"); TEST_ASSERT_EQUAL_INT(4, inlen); } /* 8) Output buffer too small for any progress on entity */ void test_htmlEncodeEntities_output_too_small_no_progress(void) { const char *in = "&"; int inlen = 1; unsigned char outbuf[1]; int outlen = 1; int rc = htmlEncodeEntities(outbuf, &outlen, (const unsigned char*)in, &inlen, 0); TEST_ASSERT_EQUAL_INT(0, rc); TEST_ASSERT_EQUAL_INT(0, outlen); TEST_ASSERT_EQUAL_INT(0, inlen); } /* 9) Partial progress: enough for 'A' but not for '&' entity */ void test_htmlEncodeEntities_partial_progress_due_to_output_limit(void) { const char *in = "A&B"; int inlen = 3; unsigned char outbuf[1]; /* fits only 'A' */ int outlen = 1; int rc = htmlEncodeEntities(outbuf, &outlen, (const unsigned char*)in, &inlen, 0); TEST_ASSERT_EQUAL_INT(0, rc); TEST_ASSERT_EQUAL_INT(1, outlen); TEST_ASSERT_EQUAL_INT(1, inlen); TEST_ASSERT_EQUAL_UINT8('A', outbuf[0]); } /* 10) Malformed UTF-8: trailing byte in leading position (0x80) */ void test_htmlEncodeEntities_malformed_trailing_in_leading_pos(void) { const unsigned char inbytes[] = {0x80}; int inlen = 1; unsigned char outbuf[16]; int outlen = (int)sizeof(outbuf); int rc = htmlEncodeEntities(outbuf, &outlen, inbytes, &inlen, 0); TEST_ASSERT_EQUAL_INT(-2, rc); TEST_ASSERT_EQUAL_INT(0, outlen); TEST_ASSERT_EQUAL_INT(0, inlen); } /* 11) Truncated multibyte sequence at end: 'A' then 0xC2 (incomplete) */ void test_htmlEncodeEntities_truncated_multibyte_at_end(void) { const unsigned char inbytes[] = {'A', 0xC2}; int inlen = 2; unsigned char outbuf[16]; int outlen = (int)sizeof(outbuf); int rc = htmlEncodeEntities(outbuf, &outlen, inbytes, &inlen, 0); TEST_ASSERT_EQUAL_INT(0, rc); TEST_ASSERT_EQUAL_INT(1, outlen); TEST_ASSERT_EQUAL_INT(1, inlen); TEST_ASSERT_EQUAL_UINT8('A', outbuf[0]); } /* 12) Invalid trailing byte in multibyte sequence: 'A' 0xC2 'A' */ void test_htmlEncodeEntities_invalid_trailing_byte(void) { const unsigned char inbytes[] = {'A', 0xC2, 'A'}; int inlen = 3; unsigned char outbuf[16]; int outlen = (int)sizeof(outbuf); int rc = htmlEncodeEntities(outbuf, &outlen, inbytes, &inlen, 0); TEST_ASSERT_EQUAL_INT(-2, rc); TEST_ASSERT_EQUAL_INT(1, outlen); /* only 'A' copied before error */ TEST_ASSERT_EQUAL_INT(1, inlen); /* only first 'A' consumed */ } /* 13) Mixed content: pass ASCII, encode specials and non-ASCII */ void test_htmlEncodeEntities_mixed_content(void) { /* "A&©🙂" */ const unsigned char inbytes[] = {'A', '&', 0xC2, 0xA9, '<', 'B', '>', 0xF0, 0x9F, 0x99, 0x82}; int inlen = (int)sizeof(inbytes); unsigned char outbuf[128]; int outlen = (int)sizeof(outbuf); int rc = htmlEncodeEntities(outbuf, &outlen, inbytes, &inlen, 0); TEST_ASSERT_EQUAL_INT(0, rc); assert_output_equals(outbuf, outlen, "A&©<B>🙂"); TEST_ASSERT_EQUAL_INT((int)sizeof(inbytes), inlen); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_htmlEncodeEntities_null_args); RUN_TEST(test_htmlEncodeEntities_ascii_passthrough); RUN_TEST(test_htmlEncodeEntities_special_ascii_entities); RUN_TEST(test_htmlEncodeEntities_double_quote_encoded); RUN_TEST(test_htmlEncodeEntities_single_quote_encoded); RUN_TEST(test_htmlEncodeEntities_named_entity_copyright); RUN_TEST(test_htmlEncodeEntities_numeric_entity_smiley); RUN_TEST(test_htmlEncodeEntities_output_too_small_no_progress); RUN_TEST(test_htmlEncodeEntities_partial_progress_due_to_output_limit); RUN_TEST(test_htmlEncodeEntities_malformed_trailing_in_leading_pos); RUN_TEST(test_htmlEncodeEntities_truncated_multibyte_at_end); RUN_TEST(test_htmlEncodeEntities_invalid_trailing_byte); RUN_TEST(test_htmlEncodeEntities_mixed_content); return UNITY_END(); }