#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();
}