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