#include "unity/unity.h" #include #include #include /* Use public prototypes only; wrapper provided in module */ extern int test_htmlParseElementInternal(htmlParserCtxtPtr ctxt); typedef struct { int startCount; int endCount; char lastStart[64]; char lastEnd[64]; } TestSaxCounters; static void sax_start(void *ctx, const xmlChar *name, const xmlChar **atts) { (void)atts; TestSaxCounters *c = (TestSaxCounters *)ctx; c->startCount++; if (name != NULL) { strncpy(c->lastStart, (const char *)name, sizeof(c->lastStart) - 1); c->lastStart[sizeof(c->lastStart) - 1] = '\0'; } } static void sax_end(void *ctx, const xmlChar *name) { TestSaxCounters *c = (TestSaxCounters *)ctx; c->endCount++; if (name != NULL) { strncpy(c->lastEnd, (const char *)name, sizeof(c->lastEnd) - 1); c->lastEnd[sizeof(c->lastEnd) - 1] = '\0'; } } static htmlParserCtxtPtr make_ctxt_with_input(const char *input, int use_html5, xmlSAXHandler *sax, void *user) { htmlParserCtxtPtr ctxt = htmlCreatePushParserCtxt(sax, user, input, (int)strlen(input), NULL, XML_CHAR_ENCODING_NONE); TEST_ASSERT_NOT_NULL_MESSAGE(ctxt, "Failed to create HTML push parser context"); if (use_html5) { ctxt->options |= HTML_PARSE_HTML5; } return ctxt; } void setUp(void) { xmlInitParser(); } void tearDown(void) { /* No global cleanup here; libxml cleanup is handled externally if needed */ } /* Tests */ void test_htmlParseElementInternal_null_context_returns_0(void) { int ret = test_htmlParseElementInternal(NULL); TEST_ASSERT_EQUAL_INT(0, ret); } void test_htmlParseElementInternal_null_input_returns_0(void) { htmlParserCtxtPtr ctxt = htmlNewParserCtxt(); TEST_ASSERT_NOT_NULL(ctxt); /* Ensure input is NULL */ TEST_ASSERT_NULL(ctxt->input); int ret = test_htmlParseElementInternal(ctxt); TEST_ASSERT_EQUAL_INT(0, ret); htmlFreeParserCtxt(ctxt); } void test_htmlParseElementInternal_nonempty_tag_returns_1_and_callbacks(void) { const char *src = "
"; xmlSAXHandler sax; memset(&sax, 0, sizeof(sax)); sax.startElement = sax_start; sax.endElement = sax_end; TestSaxCounters counters = {0}; htmlParserCtxtPtr ctxt = make_ctxt_with_input(src, 0, &sax, &counters); int ret = test_htmlParseElementInternal(ctxt); TEST_ASSERT_EQUAL_INT(1, ret); TEST_ASSERT_EQUAL_INT(1, counters.startCount); TEST_ASSERT_EQUAL_INT(0, counters.endCount); TEST_ASSERT_EQUAL_STRING("div", counters.lastStart); /* Name should remain as current element since not empty */ TEST_ASSERT_NOT_NULL(ctxt->name); TEST_ASSERT_TRUE(xmlStrEqual(ctxt->name, BAD_CAST "div")); htmlFreeParserCtxt(ctxt); } void test_htmlParseElementInternal_self_closing_non_html5_calls_endElement_and_returns_0(void) { const char *src = ""; xmlSAXHandler sax; memset(&sax, 0, sizeof(sax)); sax.startElement = sax_start; sax.endElement = sax_end; TestSaxCounters counters = {0}; htmlParserCtxtPtr ctxt = make_ctxt_with_input(src, 0, &sax, &counters); int ret = test_htmlParseElementInternal(ctxt); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_EQUAL_INT(1, counters.startCount); TEST_ASSERT_EQUAL_INT(1, counters.endCount); TEST_ASSERT_EQUAL_STRING("input", counters.lastStart); TEST_ASSERT_EQUAL_STRING("input", counters.lastEnd); htmlFreeParserCtxt(ctxt); } void test_htmlParseElementInternal_self_closing_html5_does_not_call_endElement_returns_0(void) { const char *src = ""; xmlSAXHandler sax; memset(&sax, 0, sizeof(sax)); sax.startElement = sax_start; sax.endElement = sax_end; TestSaxCounters counters = {0}; htmlParserCtxtPtr ctxt = make_ctxt_with_input(src, 1, &sax, &counters); int ret = test_htmlParseElementInternal(ctxt); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_EQUAL_INT(1, counters.startCount); TEST_ASSERT_EQUAL_INT(0, counters.endCount); TEST_ASSERT_EQUAL_STRING("input", counters.lastStart); htmlFreeParserCtxt(ctxt); } void test_htmlParseElementInternal_dtd_empty_without_solidus_returns_0_and_calls_end(void) { const char *src = "
"; xmlSAXHandler sax; memset(&sax, 0, sizeof(sax)); sax.startElement = sax_start; sax.endElement = sax_end; TestSaxCounters counters = {0}; htmlParserCtxtPtr ctxt = make_ctxt_with_input(src, 0, &sax, &counters); int ret = test_htmlParseElementInternal(ctxt); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_EQUAL_INT(1, counters.startCount); TEST_ASSERT_EQUAL_INT(1, counters.endCount); TEST_ASSERT_EQUAL_STRING("br", counters.lastStart); TEST_ASSERT_EQUAL_STRING("br", counters.lastEnd); htmlFreeParserCtxt(ctxt); } void test_htmlParseElementInternal_missing_gt_returns_0_and_no_callbacks(void) { const char *src = "endCheckState); int ret = test_htmlParseElementInternal(ctxt); TEST_ASSERT_EQUAL_INT(1, ret); TEST_ASSERT_EQUAL_INT(1, counters.startCount); TEST_ASSERT_EQUAL_INT(0, counters.endCount); TEST_ASSERT_EQUAL_INT(0, ctxt->endCheckState); /* remains unchanged */ htmlFreeParserCtxt(ctxt); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_htmlParseElementInternal_null_context_returns_0); RUN_TEST(test_htmlParseElementInternal_null_input_returns_0); RUN_TEST(test_htmlParseElementInternal_nonempty_tag_returns_1_and_callbacks); RUN_TEST(test_htmlParseElementInternal_self_closing_non_html5_calls_endElement_and_returns_0); RUN_TEST(test_htmlParseElementInternal_self_closing_html5_does_not_call_endElement_returns_0); RUN_TEST(test_htmlParseElementInternal_dtd_empty_without_solidus_returns_0_and_calls_end); RUN_TEST(test_htmlParseElementInternal_missing_gt_returns_0_and_no_callbacks); RUN_TEST(test_htmlParseElementInternal_unknown_tag_keeps_endCheckState_zero_returns_1); return UNITY_END(); }