|
|
#include "unity/unity.h" |
|
|
#include <libxml/HTMLtree.h> |
|
|
|
|
|
#include <libxml/tree.h> |
|
|
#include <libxml/xmlIO.h> |
|
|
#include <stdlib.h> |
|
|
#include <string.h> |
|
|
|
|
|
|
|
|
void test_htmlAttrDumpOutput(xmlOutputBufferPtr buf, xmlAttrPtr cur); |
|
|
|
|
|
|
|
|
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; |
|
|
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) { |
|
|
|
|
|
} |
|
|
|
|
|
void tearDown(void) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
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; |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
xmlOutputBufferClose(buf); |
|
|
|
|
|
const char *expected = " title=\"Tom & "Jerry"\""; |
|
|
TEST_ASSERT_NOT_NULL(acc.data); |
|
|
TEST_ASSERT_EQUAL_STRING(expected, acc.data); |
|
|
|
|
|
Accum_free(&acc); |
|
|
xmlFreeDoc(doc); |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
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&beta\""; |
|
|
TEST_ASSERT_NOT_NULL(acc.data); |
|
|
TEST_ASSERT_EQUAL_STRING(expected, acc.data); |
|
|
|
|
|
Accum_free(&acc); |
|
|
xmlFreeDoc(doc); |
|
|
} |
|
|
|
|
|
|
|
|
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&"Z\""; |
|
|
TEST_ASSERT_NOT_NULL(acc.data); |
|
|
TEST_ASSERT_EQUAL_STRING(expected, acc.data); |
|
|
|
|
|
Accum_free(&acc); |
|
|
xmlFreeDoc(doc); |
|
|
} |
|
|
|
|
|
|
|
|
void test_htmlAttrDumpOutput_entity_ref_in_value(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); |
|
|
|
|
|
xmlNodePtr t1 = xmlNewText(BAD_CAST "X"); |
|
|
xmlNodePtr ref = xmlNewReference(doc, BAD_CAST "copy"); |
|
|
xmlNodePtr t2 = xmlNewText(BAD_CAST "Y"); |
|
|
TEST_ASSERT_NOT_NULL(t1); |
|
|
TEST_ASSERT_NOT_NULL(ref); |
|
|
TEST_ASSERT_NOT_NULL(t2); |
|
|
|
|
|
|
|
|
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©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(); |
|
|
} |