#include "unity/unity.h" #include #include #include /* Wrapper for the static function provided in the module */ int test_htmlParseCharData(htmlParserCtxtPtr ctxt, int partial); /* Helper to create a parser context from a buffer with explicit length */ static htmlParserCtxtPtr make_ctxt_buf(const char *buf, int len) { htmlParserCtxtPtr ctxt = htmlCreateMemoryParserCtxt(buf, len); TEST_ASSERT_NOT_NULL_MESSAGE(ctxt, "htmlCreateMemoryParserCtxt returned NULL"); TEST_ASSERT_NOT_NULL_MESSAGE(ctxt->input, "Parser context has no input"); /* Disable SAX to avoid invoking htmlStartCharData and other callbacks */ ctxt->sax = NULL; ctxt->disableSAX = 1; /* Start in normal data mode */ ctxt->endCheckState = 0; /* Initialize position tracking to deterministic values */ ctxt->input->line = 1; ctxt->input->col = 1; return ctxt; } /* Convenience helper for NUL-terminated strings */ static htmlParserCtxtPtr make_ctxt_str(const char *s) { return make_ctxt_buf(s, (int)strlen(s)); } void setUp(void) { /* Optionally initialize libxml2 once here if needed */ } void tearDown(void) { /* Optional per-test cleanup */ } /* Basic: In DATA mode (mode == 0), stop at '<' and return complete=1 */ static void test_htmlParseCharData_stops_at_lt_and_returns_complete(void) { const char *data = "helloinput); TEST_ASSERT_TRUE(ctxt->input->cur < ctxt->input->end); TEST_ASSERT_EQUAL_CHAR('<', (char)*ctxt->input->cur); /* When complete, endCheckState is reset to 0 */ TEST_ASSERT_EQUAL_INT(0, ctxt->endCheckState); htmlFreeParserCtxt(ctxt); } /* Partial mode: Incomplete entity at end should not consume '&' and should return complete=0 */ static void test_htmlParseCharData_partial_incomplete_entity_not_consumed(void) { const char *data = "&"; htmlParserCtxtPtr ctxt = make_ctxt_str(data); int ret = test_htmlParseCharData(ctxt, 1); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_TRUE(ctxt->input->cur < ctxt->input->end); TEST_ASSERT_EQUAL_CHAR('&', (char)*ctxt->input->cur); /* Not complete; endCheckState remains the current mode (0) */ TEST_ASSERT_EQUAL_INT(0, ctxt->endCheckState); htmlFreeParserCtxt(ctxt); } /* DATA mode: NUL byte is stripped; then parser stops at '<' */ static void test_htmlParseCharData_consumes_nul_then_stops_at_lt(void) { const char raw[] = { '\0', '<', 'a', 'b', 'c' }; htmlParserCtxtPtr ctxt = make_ctxt_buf(raw, (int)sizeof(raw)); int ret = test_htmlParseCharData(ctxt, 0); TEST_ASSERT_EQUAL_INT(1, ret); TEST_ASSERT_TRUE(ctxt->input->cur < ctxt->input->end); TEST_ASSERT_EQUAL_CHAR('<', (char)*ctxt->input->cur); TEST_ASSERT_EQUAL_INT(0, ctxt->endCheckState); htmlFreeParserCtxt(ctxt); } /* CR handling: CR alone translates to LF via SAX (ignored here) and is skipped; then stop at '<' */ static void test_htmlParseCharData_cr_translated_and_skipped_then_lt(void) { const char raw[] = { '\r', '<', 'x' }; htmlParserCtxtPtr ctxt = make_ctxt_buf(raw, (int)sizeof(raw)); int ret = test_htmlParseCharData(ctxt, 0); TEST_ASSERT_EQUAL_INT(1, ret); TEST_ASSERT_TRUE(ctxt->input->cur < ctxt->input->end); TEST_ASSERT_EQUAL_CHAR('<', (char)*ctxt->input->cur); TEST_ASSERT_EQUAL_INT(0, ctxt->endCheckState); htmlFreeParserCtxt(ctxt); } /* Numeric character reference: should be consumed and continue to '<' */ static void test_htmlParseCharData_consumes_numeric_char_ref(void) { const char *data = "ABC<"; htmlParserCtxtPtr ctxt = make_ctxt_str(data); int ret = test_htmlParseCharData(ctxt, 0); TEST_ASSERT_EQUAL_INT(1, ret); TEST_ASSERT_TRUE(ctxt->input->cur < ctxt->input->end); TEST_ASSERT_EQUAL_CHAR('<', (char)*ctxt->input->cur); TEST_ASSERT_EQUAL_INT(0, ctxt->endCheckState); htmlFreeParserCtxt(ctxt); } /* Non-zero mode: complete when encountering a matching closing tag like

*/ static void test_htmlParseCharData_nonzero_mode_stops_at_matching_closing_tag(void) { const char *data = "abc

xyz"; htmlParserCtxtPtr ctxt = make_ctxt_str(data); /* Simulate being inside a

element by setting name and a non-zero mode */ ctxt->name = (xmlChar *)"p"; ctxt->endCheckState = 1; /* Any non-zero mode that's not DATA */ int ret = test_htmlParseCharData(ctxt, 0); TEST_ASSERT_EQUAL_INT(1, ret); TEST_ASSERT_TRUE(ctxt->input->cur < ctxt->input->end); TEST_ASSERT_EQUAL_CHAR('<', (char)*ctxt->input->cur); /* complete -> endCheckState reset to 0 */ TEST_ASSERT_EQUAL_INT(0, ctxt->endCheckState); htmlFreeParserCtxt(ctxt); } /* No terminating '<': consume to EOF, return complete=0 */ static void test_htmlParseCharData_consumes_until_eof_when_no_lt(void) { const char *data = "abcdef"; htmlParserCtxtPtr ctxt = make_ctxt_str(data); int ret = test_htmlParseCharData(ctxt, 0); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_TRUE(ctxt->input->cur == ctxt->input->end); /* Not complete; endCheckState remains the current mode (0) */ TEST_ASSERT_EQUAL_INT(0, ctxt->endCheckState); htmlFreeParserCtxt(ctxt); } int main(void) { /* Initialize libxml2 once for all tests */ xmlInitParser(); UNITY_BEGIN(); RUN_TEST(test_htmlParseCharData_stops_at_lt_and_returns_complete); RUN_TEST(test_htmlParseCharData_partial_incomplete_entity_not_consumed); RUN_TEST(test_htmlParseCharData_consumes_nul_then_stops_at_lt); RUN_TEST(test_htmlParseCharData_cr_translated_and_skipped_then_lt); RUN_TEST(test_htmlParseCharData_consumes_numeric_char_ref); RUN_TEST(test_htmlParseCharData_nonzero_mode_stops_at_matching_closing_tag); RUN_TEST(test_htmlParseCharData_consumes_until_eof_when_no_lt); int res = UNITY_END(); xmlCleanupParser(); return res; }