libxml / tests /tests_HTMLparser_htmlParserFinishElementParsing.c
AryaWu's picture
Upload folder using huggingface_hub
6baed57 verified
#include "unity/unity.h"
#include <libxml/HTMLparser.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xmlmemory.h>
#include <stdlib.h>
#include <string.h>
/* Wrapper for the static function under test provided in the module */
extern void test_htmlParserFinishElementParsing(htmlParserCtxtPtr ctxt);
static htmlParserCtxtPtr
create_ctxt_with_input(size_t base_len, size_t cur_offset, unsigned long consumed, unsigned long line,
xmlParserInputPtr *outInput, xmlChar **outBase) {
htmlParserCtxtPtr ctxt = htmlNewParserCtxt();
TEST_ASSERT_NOT_NULL_MESSAGE(ctxt, "Failed to create HTML parser context");
xmlParserInputPtr in = xmlNewInputStream(ctxt);
TEST_ASSERT_NOT_NULL_MESSAGE(in, "Failed to create new input stream");
xmlChar *base = (xmlChar *)xmlMalloc(base_len);
TEST_ASSERT_NOT_NULL_MESSAGE(base, "Failed to allocate base buffer");
memset(base, 'A', base_len);
/* Ensure cur_offset within base_len */
TEST_ASSERT_MESSAGE(cur_offset <= base_len, "cur_offset exceeds base buffer length");
in->base = base;
in->cur = base + cur_offset;
in->end = base + base_len;
in->consumed = consumed;
in->line = line;
ctxt->input = in;
if (outInput) *outInput = in;
if (outBase) *outBase = base;
return ctxt;
}
static void
destroy_ctxt_and_input(htmlParserCtxtPtr ctxt, xmlParserInputPtr in, xmlChar *base) {
if (ctxt != NULL) {
/* Detach input from context so we can free it explicitly to avoid double-free */
ctxt->input = NULL;
}
if (in != NULL) {
/* Avoid freeing base twice if libxml would attempt it; we own base */
in->base = NULL;
in->cur = NULL;
in->end = NULL;
xmlFreeInputStream(in);
}
if (base != NULL) {
xmlFree(base);
}
if (ctxt != NULL) {
htmlFreeParserCtxt(ctxt);
}
}
void setUp(void) {
xmlInitParser();
}
void tearDown(void) {
xmlCleanupParser();
}
void test_htmlParserFinishElementParsing_records_end_info_basic(void) {
xmlParserInputPtr in = NULL;
xmlChar *base = NULL;
/* base length 32, cur_offset 5, consumed 2, line 7 */
htmlParserCtxtPtr ctxt = create_ctxt_with_input(32, 5, 2, 7, &in, &base);
/* Prepare node and nodeInfo */
xmlNodePtr node = xmlNewNode(NULL, (const xmlChar *)"test");
TEST_ASSERT_NOT_NULL(node);
ctxt->node = node;
ctxt->record_info = 1;
xmlParserNodeInfo *info = (xmlParserNodeInfo *)xmlMalloc(sizeof(xmlParserNodeInfo));
TEST_ASSERT_NOT_NULL(info);
memset(info, 0, sizeof(*info));
ctxt->nodeInfo = info;
/* Call the wrapper */
test_htmlParserFinishElementParsing(ctxt);
/* Verify info was added to the context's node info sequence */
const xmlParserNodeInfo *found = xmlParserFindNodeInfo(ctxt, node);
TEST_ASSERT_NOT_NULL_MESSAGE(found, "xmlParserNodeInfo not found for node");
TEST_ASSERT_EQUAL_PTR(node, found->node);
/* end_pos = consumed + (cur - base) = 2 + (5) = 7 */
TEST_ASSERT_EQUAL_UINT(7u, (unsigned int)found->end_pos);
TEST_ASSERT_EQUAL_UINT(7u, (unsigned int)found->end_line);
/* Cleanup */
xmlFreeNode(node);
/* info was likely freed by htmlNodeInfoPop; freeing only if still referenced would be unsafe.
We won't free 'info' here. */
destroy_ctxt_and_input(ctxt, in, base);
}
void test_htmlParserFinishElementParsing_calculates_end_pos_with_consumed_offset(void) {
xmlParserInputPtr in = NULL;
xmlChar *base = NULL;
/* base length 64, cur_offset 13, consumed 21, line 3 */
htmlParserCtxtPtr ctxt = create_ctxt_with_input(64, 13, 21, 3, &in, &base);
xmlNodePtr node = xmlNewNode(NULL, (const xmlChar *)"elem");
TEST_ASSERT_NOT_NULL(node);
ctxt->node = node;
ctxt->record_info = 1;
xmlParserNodeInfo *info = (xmlParserNodeInfo *)xmlMalloc(sizeof(xmlParserNodeInfo));
TEST_ASSERT_NOT_NULL(info);
memset(info, 0, sizeof(*info));
ctxt->nodeInfo = info;
test_htmlParserFinishElementParsing(ctxt);
const xmlParserNodeInfo *found = xmlParserFindNodeInfo(ctxt, node);
TEST_ASSERT_NOT_NULL_MESSAGE(found, "xmlParserNodeInfo not found for node");
TEST_ASSERT_EQUAL_PTR(node, found->node);
/* end_pos = 21 + 13 = 34; end_line = 3 */
TEST_ASSERT_EQUAL_UINT(34u, (unsigned int)found->end_pos);
TEST_ASSERT_EQUAL_UINT(3u, (unsigned int)found->end_line);
xmlFreeNode(node);
destroy_ctxt_and_input(ctxt, in, base);
}
void test_htmlParserFinishElementParsing_noop_when_node_null(void) {
xmlParserInputPtr in = NULL;
xmlChar *base = NULL;
htmlParserCtxtPtr ctxt = create_ctxt_with_input(16, 4, 9, 11, &in, &base);
/* node is NULL; enable record_info to test the node NULL guard */
ctxt->node = NULL;
ctxt->record_info = 1;
/* Provide a nodeInfo struct; function must not use it since node is NULL */
xmlParserNodeInfo *info = (xmlParserNodeInfo *)xmlMalloc(sizeof(xmlParserNodeInfo));
TEST_ASSERT_NOT_NULL(info);
memset(info, 0, sizeof(*info));
ctxt->nodeInfo = info;
test_htmlParserFinishElementParsing(ctxt);
/* Create a node to search for; should not find any info since node was NULL */
xmlNodePtr node = xmlNewNode(NULL, (const xmlChar *)"unused");
TEST_ASSERT_NOT_NULL(node);
const xmlParserNodeInfo *found = xmlParserFindNodeInfo(ctxt, node);
TEST_ASSERT_NULL(found);
xmlFreeNode(node);
/* As above, assume htmlNodeInfoPop may have freed info; do not free directly. */
destroy_ctxt_and_input(ctxt, in, base);
}
void test_htmlParserFinishElementParsing_noop_when_record_info_false_even_with_node(void) {
/* Intentionally no input to ensure function doesn't touch input when record_info == 0 */
htmlParserCtxtPtr ctxt = htmlNewParserCtxt();
TEST_ASSERT_NOT_NULL(ctxt);
xmlNodePtr node = xmlNewNode(NULL, (const xmlChar *)"n");
TEST_ASSERT_NOT_NULL(node);
ctxt->node = node;
ctxt->record_info = 0; /* disabled */
ctxt->input = NULL; /* would crash if accessed */
/* Provide a nodeInfo pointer; must not be used since record_info == 0 */
xmlParserNodeInfo *info = (xmlParserNodeInfo *)xmlMalloc(sizeof(xmlParserNodeInfo));
TEST_ASSERT_NOT_NULL(info);
memset(info, 0, sizeof(*info));
ctxt->nodeInfo = info;
test_htmlParserFinishElementParsing(ctxt);
/* Since recording is disabled, no node info should be present */
const xmlParserNodeInfo *found = xmlParserFindNodeInfo(ctxt, node);
TEST_ASSERT_NULL(found);
xmlFreeNode(node);
/* Do not free 'info' as it may be managed by internal pop logic in other cases. */
htmlFreeParserCtxt(ctxt);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_htmlParserFinishElementParsing_records_end_info_basic);
RUN_TEST(test_htmlParserFinishElementParsing_calculates_end_pos_with_consumed_offset);
RUN_TEST(test_htmlParserFinishElementParsing_noop_when_node_null);
RUN_TEST(test_htmlParserFinishElementParsing_noop_when_record_info_false_even_with_node);
return UNITY_END();
}