libxml / tests /tests_HTMLtree_htmlAttrDumpOutput.c
AryaWu's picture
Upload folder using huggingface_hub
6baed57 verified
#include "unity/unity.h"
#include <libxml/HTMLtree.h>
#include <libxml/tree.h>
#include <libxml/xmlIO.h>
#include <stdlib.h>
#include <string.h>
/* Wrapper provided in module to access the static function */
void test_htmlAttrDumpOutput(xmlOutputBufferPtr buf, xmlAttrPtr cur);
/* Accumulator for xmlOutputBuffer IO */
typedef struct {
char *data;
size_t len;
size_t cap;
} Accum;
static void Accum_init(Accum *a) {
a->data = NULL;
a->len = 0;
a->cap = 0;
}
static void Accum_free(Accum *a) {
free(a->data);
a->data = NULL;
a->len = 0;
a->cap = 0;
}
static int acc_write(void *context, const char *buffer, int len) {
Accum *a = (Accum *)context;
if (len <= 0)
return 0;
size_t need = a->len + (size_t)len + 1; /* +1 for NUL */
if (need > a->cap) {
size_t newcap = a->cap ? a->cap : 64;
while (newcap < need)
newcap *= 2;
char *nd = (char *)realloc(a->data, newcap);
if (nd == NULL)
return -1;
a->data = nd;
a->cap = newcap;
}
memcpy(a->data + a->len, buffer, (size_t)len);
a->len += (size_t)len;
a->data[a->len] = '\0';
return len;
}
static int acc_close(void *context) {
(void)context;
return 0;
}
static xmlOutputBufferPtr makeOutputBuffer(Accum *a) {
Accum_init(a);
xmlOutputBufferPtr buf = xmlOutputBufferCreateIO(acc_write, acc_close, a, NULL);
return buf;
}
void setUp(void) {
/* Setup code here, or leave empty */
}
void tearDown(void) {
/* Cleanup code here, or leave empty */
}
/* Helper to create a simple document and root node */
static void make_doc_and_root(const char *rootName, xmlDocPtr *pdoc, xmlNodePtr *proot) {
xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0");
TEST_ASSERT_NOT_NULL_MESSAGE(doc, "xmlNewDoc failed");
xmlNodePtr root = xmlNewNode(NULL, BAD_CAST rootName);
TEST_ASSERT_NOT_NULL_MESSAGE(root, "xmlNewNode failed");
xmlDocSetRootElement(doc, root);
*pdoc = doc;
*proot = root;
}
/* 1) Non-URI attribute with characters requiring HTML escaping */
void test_htmlAttrDumpOutput_basic_escape_text(void) {
xmlDocPtr doc = NULL;
xmlNodePtr node = NULL;
make_doc_and_root("div", &doc, &node);
xmlAttrPtr attr = xmlNewProp(node, BAD_CAST "title", BAD_CAST "Tom & \"Jerry\"");
TEST_ASSERT_NOT_NULL(attr);
Accum acc;
xmlOutputBufferPtr buf = makeOutputBuffer(&acc);
TEST_ASSERT_NOT_NULL(buf);
test_htmlAttrDumpOutput(buf, attr);
xmlOutputBufferFlush(buf);
/* Don't use buf after close */
xmlOutputBufferClose(buf);
const char *expected = " title=\"Tom &amp; &quot;Jerry&quot;\"";
TEST_ASSERT_NOT_NULL(acc.data);
TEST_ASSERT_EQUAL_STRING(expected, acc.data);
Accum_free(&acc);
xmlFreeDoc(doc);
}
/* 2) Boolean attribute (lowercase) should serialize without ="..." */
void test_htmlAttrDumpOutput_boolean_selected_lowercase(void) {
xmlDocPtr doc = NULL;
xmlNodePtr node = NULL;
make_doc_and_root("option", &doc, &node);
xmlAttrPtr attr = xmlNewProp(node, BAD_CAST "selected", BAD_CAST "selected");
TEST_ASSERT_NOT_NULL(attr);
Accum acc;
xmlOutputBufferPtr buf = makeOutputBuffer(&acc);
TEST_ASSERT_NOT_NULL(buf);
test_htmlAttrDumpOutput(buf, attr);
xmlOutputBufferFlush(buf);
xmlOutputBufferClose(buf);
const char *expected = " selected";
TEST_ASSERT_NOT_NULL(acc.data);
TEST_ASSERT_EQUAL_STRING(expected, acc.data);
Accum_free(&acc);
xmlFreeDoc(doc);
}
/* 3) Boolean attribute (uppercase name) should also serialize without ="..." */
void test_htmlAttrDumpOutput_boolean_selected_uppercase(void) {
xmlDocPtr doc = NULL;
xmlNodePtr node = NULL;
make_doc_and_root("option", &doc, &node);
xmlAttrPtr attr = xmlNewProp(node, BAD_CAST "SELECTED", BAD_CAST "true");
TEST_ASSERT_NOT_NULL(attr);
Accum acc;
xmlOutputBufferPtr buf = makeOutputBuffer(&acc);
TEST_ASSERT_NOT_NULL(buf);
test_htmlAttrDumpOutput(buf, attr);
xmlOutputBufferFlush(buf);
xmlOutputBufferClose(buf);
const char *expected = " SELECTED";
TEST_ASSERT_NOT_NULL(acc.data);
TEST_ASSERT_EQUAL_STRING(expected, acc.data);
Accum_free(&acc);
xmlFreeDoc(doc);
}
/* 4) Attribute with NULL value (no children) should serialize without ="..." */
void test_htmlAttrDumpOutput_null_value_no_children(void) {
xmlDocPtr doc = NULL;
xmlNodePtr node = NULL;
make_doc_and_root("div", &doc, &node);
xmlAttrPtr attr = xmlNewProp(node, BAD_CAST "data", NULL);
TEST_ASSERT_NOT_NULL(attr);
TEST_ASSERT_NULL(attr->children);
Accum acc;
xmlOutputBufferPtr buf = makeOutputBuffer(&acc);
TEST_ASSERT_NOT_NULL(buf);
test_htmlAttrDumpOutput(buf, attr);
xmlOutputBufferFlush(buf);
xmlOutputBufferClose(buf);
const char *expected = " data";
TEST_ASSERT_NOT_NULL(acc.data);
TEST_ASSERT_EQUAL_STRING(expected, acc.data);
Accum_free(&acc);
xmlFreeDoc(doc);
}
/* 5) Namespaced attribute: prefix should be printed and value escaped */
void test_htmlAttrDumpOutput_namespaced_attr_with_escape(void) {
xmlDocPtr doc = NULL;
xmlNodePtr node = NULL;
make_doc_and_root("div", &doc, &node);
xmlNsPtr ns = xmlNewNs(node, BAD_CAST "http://example.com/ns", BAD_CAST "p");
TEST_ASSERT_NOT_NULL(ns);
xmlAttrPtr attr = xmlNewNsProp(node, ns, BAD_CAST "id", BAD_CAST "alpha&beta");
TEST_ASSERT_NOT_NULL(attr);
Accum acc;
xmlOutputBufferPtr buf = makeOutputBuffer(&acc);
TEST_ASSERT_NOT_NULL(buf);
test_htmlAttrDumpOutput(buf, attr);
xmlOutputBufferFlush(buf);
xmlOutputBufferClose(buf);
const char *expected = " p:id=\"alpha&amp;beta\"";
TEST_ASSERT_NOT_NULL(acc.data);
TEST_ASSERT_EQUAL_STRING(expected, acc.data);
Accum_free(&acc);
xmlFreeDoc(doc);
}
/* 6) URI attribute (href): leading spaces preserved, spaces encoded as %20; '&' and '\"' escaped */
void test_htmlAttrDumpOutput_uri_href_serialization(void) {
xmlDocPtr doc = NULL;
xmlNodePtr node = NULL;
make_doc_and_root("a", &doc, &node);
xmlAttrPtr attr = xmlNewProp(node, BAD_CAST "href", BAD_CAST " Ab c&\"Z");
TEST_ASSERT_NOT_NULL(attr);
Accum acc;
xmlOutputBufferPtr buf = makeOutputBuffer(&acc);
TEST_ASSERT_NOT_NULL(buf);
test_htmlAttrDumpOutput(buf, attr);
xmlOutputBufferFlush(buf);
xmlOutputBufferClose(buf);
const char *expected = " href=\" Ab%20c&amp;&quot;Z\"";
TEST_ASSERT_NOT_NULL(acc.data);
TEST_ASSERT_EQUAL_STRING(expected, acc.data);
Accum_free(&acc);
xmlFreeDoc(doc);
}
/* 7) Attribute value containing an entity reference node should serialize as &name; */
void test_htmlAttrDumpOutput_entity_ref_in_value(void) {
xmlDocPtr doc = NULL;
xmlNodePtr node = NULL;
make_doc_and_root("div", &doc, &node);
/* Start with an attribute without children, then build a mixed content value */
xmlAttrPtr attr = xmlNewProp(node, BAD_CAST "data", NULL);
TEST_ASSERT_NOT_NULL(attr);
xmlNodePtr t1 = xmlNewText(BAD_CAST "X");
xmlNodePtr ref = xmlNewReference(doc, BAD_CAST "copy"); /* &copy; */
xmlNodePtr t2 = xmlNewText(BAD_CAST "Y");
TEST_ASSERT_NOT_NULL(t1);
TEST_ASSERT_NOT_NULL(ref);
TEST_ASSERT_NOT_NULL(t2);
/* Attach children to the attribute */
xmlAddChild((xmlNodePtr)attr, t1);
xmlAddChild((xmlNodePtr)attr, ref);
xmlAddChild((xmlNodePtr)attr, t2);
Accum acc;
xmlOutputBufferPtr buf = makeOutputBuffer(&acc);
TEST_ASSERT_NOT_NULL(buf);
test_htmlAttrDumpOutput(buf, attr);
xmlOutputBufferFlush(buf);
xmlOutputBufferClose(buf);
const char *expected = " data=\"X&copy;Y\"";
TEST_ASSERT_NOT_NULL(acc.data);
TEST_ASSERT_EQUAL_STRING(expected, acc.data);
Accum_free(&acc);
xmlFreeDoc(doc);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_htmlAttrDumpOutput_basic_escape_text);
RUN_TEST(test_htmlAttrDumpOutput_boolean_selected_lowercase);
RUN_TEST(test_htmlAttrDumpOutput_boolean_selected_uppercase);
RUN_TEST(test_htmlAttrDumpOutput_null_value_no_children);
RUN_TEST(test_htmlAttrDumpOutput_namespaced_attr_with_escape);
RUN_TEST(test_htmlAttrDumpOutput_uri_href_serialization);
RUN_TEST(test_htmlAttrDumpOutput_entity_ref_in_value);
return UNITY_END();
}