#include "unity/unity.h" #include #include #include #include /* Wrapper provided in the source for calling the static function */ extern int test_htmlSkipBlankChars(xmlParserCtxtPtr ctxt); void setUp(void) { /* Ensure libxml is initialized */ xmlInitParser(); } void tearDown(void) { /* No global cleanup required per test */ } /* Helper to create a parser context with a single input over given content */ static void create_ctxt_with_content(const char *content, int init_line, int init_col, xmlParserCtxtPtr *out_ctxt, xmlParserInputPtr *out_input, xmlChar **out_buf, int *out_len) { TEST_ASSERT_NOT_NULL_MESSAGE(out_ctxt, "out_ctxt must not be NULL"); TEST_ASSERT_NOT_NULL_MESSAGE(out_input, "out_input must not be NULL"); TEST_ASSERT_NOT_NULL_MESSAGE(out_buf, "out_buf must not be NULL"); int len = (content != NULL) ? (int)strlen(content) : 0; xmlChar *buf = (xmlChar *)xmlMalloc((size_t)len + 1); TEST_ASSERT_NOT_NULL_MESSAGE(buf, "Failed to allocate buffer"); if (len > 0 && content != NULL) { memcpy(buf, content, (size_t)len); } buf[len] = 0; htmlParserCtxtPtr hctxt = htmlNewParserCtxt(); TEST_ASSERT_NOT_NULL_MESSAGE(hctxt, "Failed to create parser context"); xmlParserInputPtr input = (xmlParserInputPtr)xmlMalloc(sizeof(*input)); TEST_ASSERT_NOT_NULL_MESSAGE(input, "Failed to allocate parser input"); memset(input, 0, sizeof(*input)); input->base = buf; input->cur = buf; input->end = buf + len; input->line = init_line; input->col = init_col; hctxt->input = input; *out_ctxt = (xmlParserCtxtPtr)hctxt; *out_input = input; *out_buf = buf; if (out_len) *out_len = len; } static void destroy_ctxt_with_content(xmlParserCtxtPtr ctxt, xmlParserInputPtr input, xmlChar *buf) { if (ctxt != NULL) { /* Prevent any internal free of our manually attached input */ ctxt->input = NULL; htmlFreeParserCtxt((htmlParserCtxtPtr)ctxt); } if (input != NULL) xmlFree(input); if (buf != NULL) xmlFree(buf); } /* Test 1: Only spaces, verify count and col update */ void test_htmlSkipBlankChars_only_spaces(void) { xmlParserCtxtPtr ctxt = NULL; xmlParserInputPtr input = NULL; xmlChar *buf = NULL; create_ctxt_with_content(" abc", 3, 5, &ctxt, &input, &buf, NULL); int ret = test_htmlSkipBlankChars(ctxt); TEST_ASSERT_EQUAL_INT(4, ret); TEST_ASSERT_EQUAL_INT(3, input->line); TEST_ASSERT_EQUAL_INT(9, input->col); TEST_ASSERT_EQUAL_CHAR('a', (char)*(input->cur)); destroy_ctxt_with_content(ctxt, input, buf); } /* Test 2: Mix of newline and spaces/tabs, verify line/col logic */ void test_htmlSkipBlankChars_newlines_and_spaces(void) { xmlParserCtxtPtr ctxt = NULL; xmlParserInputPtr input = NULL; xmlChar *buf = NULL; /* Sequence: '\n', ' ', '\n', '\t', 'X' */ create_ctxt_with_content("\n \n\tX", 1, 1, &ctxt, &input, &buf, NULL); int ret = test_htmlSkipBlankChars(ctxt); TEST_ASSERT_EQUAL_INT(4, ret); TEST_ASSERT_EQUAL_INT(3, input->line); /* two newlines encountered */ TEST_ASSERT_EQUAL_INT(2, input->col); /* after last '\t' column is 2 */ TEST_ASSERT_EQUAL_CHAR('X', (char)*(input->cur)); destroy_ctxt_with_content(ctxt, input, buf); } /* Test 3: Stops at first non-whitespace and leaves cur at that char */ void test_htmlSkipBlankChars_stops_on_non_ws(void) { xmlParserCtxtPtr ctxt = NULL; xmlParserInputPtr input = NULL; xmlChar *buf = NULL; create_ctxt_with_content(" \t\rA B", 1, 1, &ctxt, &input, &buf, NULL); int ret = test_htmlSkipBlankChars(ctxt); TEST_ASSERT_EQUAL_INT(3, ret); /* space, tab, carriage return */ TEST_ASSERT_EQUAL_INT(1, input->line); /* no newline seen */ TEST_ASSERT_EQUAL_INT(4, input->col); /* 1 + 3 */ TEST_ASSERT_EQUAL_CHAR('A', (char)*(input->cur)); destroy_ctxt_with_content(ctxt, input, buf); } /* Test 4: Zero available input (cur == end) returns 0 and leaves state */ void test_htmlSkipBlankChars_zero_avail(void) { xmlParserCtxtPtr ctxt = NULL; xmlParserInputPtr input = NULL; xmlChar *buf = NULL; create_ctxt_with_content("", 10, 7, &ctxt, &input, &buf, NULL); int ret = test_htmlSkipBlankChars(ctxt); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_EQUAL_INT(10, input->line); TEST_ASSERT_EQUAL_INT(7, input->col); TEST_ASSERT_TRUE(input->cur == input->end); destroy_ctxt_with_content(ctxt, input, buf); } /* Test 5: Parser stopped -> no changes and return 0 */ void test_htmlSkipBlankChars_parser_stopped(void) { xmlParserCtxtPtr ctxt = NULL; xmlParserInputPtr input = NULL; xmlChar *buf = NULL; create_ctxt_with_content(" \n\tabc", 2, 3, &ctxt, &input, &buf, NULL); /* Force parser stopped */ ctxt->instate = XML_PARSER_EOF; int ret = test_htmlSkipBlankChars(ctxt); TEST_ASSERT_EQUAL_INT(0, ret); TEST_ASSERT_EQUAL_INT(2, input->line); TEST_ASSERT_EQUAL_INT(3, input->col); TEST_ASSERT_TRUE(input->cur == input->base); /* no advancement */ destroy_ctxt_with_content(ctxt, input, buf); } /* Test 6: Large whitespace run to also cover res > 8 (GROW path) */ void test_htmlSkipBlankChars_large_whitespace(void) { char tmp[102 + 1]; /* 100 spaces + 'Z' + NUL */ memset(tmp, ' ', 100); tmp[100] = 'Z'; tmp[101] = '\0'; xmlParserCtxtPtr ctxt = NULL; xmlParserInputPtr input = NULL; xmlChar *buf = NULL; create_ctxt_with_content(tmp, 1, 1, &ctxt, &input, &buf, NULL); int ret = test_htmlSkipBlankChars(ctxt); TEST_ASSERT_EQUAL_INT(100, ret); TEST_ASSERT_EQUAL_INT(1, input->line); TEST_ASSERT_EQUAL_INT(101, input->col); /* starting at 1, +100 spaces */ TEST_ASSERT_EQUAL_CHAR('Z', (char)*(input->cur)); destroy_ctxt_with_content(ctxt, input, buf); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_htmlSkipBlankChars_only_spaces); RUN_TEST(test_htmlSkipBlankChars_newlines_and_spaces); RUN_TEST(test_htmlSkipBlankChars_stops_on_non_ws); RUN_TEST(test_htmlSkipBlankChars_zero_avail); RUN_TEST(test_htmlSkipBlankChars_parser_stopped); RUN_TEST(test_htmlSkipBlankChars_large_whitespace); return UNITY_END(); }